diff --git a/docs/source/conf.py b/docs/source/conf.py index e770a03..dc29731 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -38,7 +38,7 @@ extensions = [ ] autodoc_member_order = 'bysource' autodoc_inherit_docstrings = False -autodoc_mock_imports = ["galaxy.http"] +autodoc_mock_imports = ["aiohttp"] set_type_checking_flag = True diff --git a/docs/source/galaxy.http.rst b/docs/source/galaxy.http.rst new file mode 100644 index 0000000..c0abec4 --- /dev/null +++ b/docs/source/galaxy.http.rst @@ -0,0 +1,8 @@ +galaxy.http +================= + +.. automodule:: galaxy.http + :members: + :special-members: __init__ + :undoc-members: + :show-inheritance: diff --git a/docs/source/index.rst b/docs/source/index.rst index 4455599..b1233f5 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -6,7 +6,8 @@ GOG Galaxy Integrations Python API :includehidden: Overview - API + galaxy.api + galaxy.http Platform ID's Index diff --git a/src/galaxy/http.py b/src/galaxy/http.py index 1e6db79..615daa0 100644 --- a/src/galaxy/http.py +++ b/src/galaxy/http.py @@ -1,3 +1,34 @@ +""" +This module standarize http traffic and the error handling for further communication with the GOG Galaxy 2.0. + +It is recommended to use provided convenient methods for HTTP requests, especially when dealing with authorized sessions. +Examplary simple web service could looks like: + + .. code-block:: python + + import logging + from galaxy.http import create_client_session, handle_exception + + class BackendClient: + AUTH_URL = 'my-integration.com/auth' + HEADERS = { + "My-Custom-Header": "true", + } + def __init__(self): + self._session = create_client_session(headers=self.HEADERS) + + async def authenticate(self): + await self._session.request('POST', self.AUTH_URL) + + async def close(self): + # to be called on plugin shutdown + await self._session.close() + + async def _authorized_request(self, method, url, *args, **kwargs): + with handle_exceptions(): + return await self._session.request(method, url, *args, **kwargs) +""" + import asyncio import ssl from contextlib import contextmanager @@ -13,17 +44,23 @@ from galaxy.api.errors import ( ) +#: Default limit of the simultaneous connections for ssl connector. DEFAULT_LIMIT = 20 -DEFAULT_TIMEOUT = 60 # seconds +#: Default timeout in seconds used for client session. +DEFAULT_TIMEOUT = 60 class HttpClient: - """Deprecated""" + """ + .. deprecated:: 0.41 + Use http module functions instead + """ def __init__(self, limit=DEFAULT_LIMIT, timeout=aiohttp.ClientTimeout(total=DEFAULT_TIMEOUT), cookie_jar=None): connector = create_tcp_connector(limit=limit) self._session = create_client_session(connector=connector, timeout=timeout, cookie_jar=cookie_jar) async def close(self): + """Closes connection. Should be called in :meth:`~galaxy.api.plugin.Plugin.shutdown`""" await self._session.close() async def request(self, method, url, *args, **kwargs): @@ -31,23 +68,50 @@ class HttpClient: return await self._session.request(method, url, *args, **kwargs) -def create_tcp_connector(*args, **kwargs): +def create_tcp_connector(*args, **kwargs) -> aiohttp.TCPConnector: + """ + Creates TCP connector with resonable defaults. + For details about available parameters refer to + `aiohttp.TCPConnector `_ + """ ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) ssl_context.load_verify_locations(certifi.where()) kwargs.setdefault("ssl", ssl_context) kwargs.setdefault("limit", DEFAULT_LIMIT) - return aiohttp.TCPConnector(*args, **kwargs) + return aiohttp.TCPConnector(*args, **kwargs) # type: ignore due to https://github.com/python/mypy/issues/4001 -def create_client_session(*args, **kwargs): +def create_client_session(*args, **kwargs) -> aiohttp.ClientSession: + """ + Creates client session with resonable defaults. + For details about available parameters refer to + `aiohttp.ClientSession `_ + + Examplary customization: + + .. code-block:: python + + from galaxy.http import create_client_session, create_tcp_connector + + session = create_client_session( + headers={ + "Keep-Alive": "true" + }, + connector=create_tcp_connector(limit=40), + timeout=100) + """ kwargs.setdefault("connector", create_tcp_connector()) kwargs.setdefault("timeout", aiohttp.ClientTimeout(total=DEFAULT_TIMEOUT)) kwargs.setdefault("raise_for_status", True) - return aiohttp.ClientSession(*args, **kwargs) + return aiohttp.ClientSession(*args, **kwargs) # type: ignore due to https://github.com/python/mypy/issues/4001 @contextmanager def handle_exception(): + """ + Context manager translating network related exceptions + to custom :mod:`~galaxy.api.errors`. + """ try: yield except asyncio.TimeoutError: @@ -78,4 +142,3 @@ def handle_exception(): except aiohttp.ClientError: logging.exception("Caught exception while performing request") raise UnknownError() - diff --git a/src/galaxy/unittest/mock.py b/src/galaxy/unittest/mock.py index d5fc0af..b439671 100644 --- a/src/galaxy/unittest/mock.py +++ b/src/galaxy/unittest/mock.py @@ -4,8 +4,8 @@ from unittest.mock import MagicMock class AsyncMock(MagicMock): """ - ..deprecated:: 0.45 - Use: :class:`MagicMock` with meth:`~.async_return_value`. + .. deprecated:: 0.45 + Use: :class:`MagicMock` with meth:`~.async_return_value`. """ async def __call__(self, *args, **kwargs): return super(AsyncMock, self).__call__(*args, **kwargs) @@ -13,8 +13,8 @@ class AsyncMock(MagicMock): def coroutine_mock(): """ - ..deprecated:: 0.45 - Use: :class:`MagicMock` with meth:`~.async_return_value`. + .. deprecated:: 0.45 + Use: :class:`MagicMock` with meth:`~.async_return_value`. """ coro = MagicMock(name="CoroutineResult") corofunc = MagicMock(name="CoroutineFunction", side_effect=asyncio.coroutine(coro))