Skip to content

Commit

Permalink
Refactor interfaces (#252)
Browse files Browse the repository at this point in the history
* Move Dispatcher, AsyncDispatcher into dispatch/base.py

* Move concurrency interfaces into concurrency/base.py
  • Loading branch information
tomchristie authored and florimondmanca committed Aug 21, 2019
1 parent 4ea4af6 commit c0554e9
Show file tree
Hide file tree
Showing 12 changed files with 162 additions and 155 deletions.
17 changes: 8 additions & 9 deletions httpx/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@
from .api import delete, get, head, options, patch, post, put, request
from .client import AsyncClient, Client
from .concurrency.asyncio import AsyncioBackend
from .concurrency.base import (
BaseBackgroundManager,
BasePoolSemaphore,
BaseReader,
BaseWriter,
ConcurrencyBackend,
)
from .config import (
USER_AGENT,
CertTypes,
Expand All @@ -13,6 +20,7 @@
TimeoutTypes,
VerifyTypes,
)
from .dispatch.base import AsyncDispatcher, Dispatcher
from .dispatch.connection import HTTPConnection
from .dispatch.connection_pool import ConnectionPool
from .exceptions import (
Expand All @@ -32,15 +40,6 @@
TooManyRedirects,
WriteTimeout,
)
from .interfaces import (
AsyncDispatcher,
BaseBackgroundManager,
BasePoolSemaphore,
BaseReader,
BaseWriter,
ConcurrencyBackend,
Dispatcher,
)
from .models import (
URL,
AsyncRequest,
Expand Down
3 changes: 2 additions & 1 deletion httpx/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

from .auth import HTTPBasicAuth
from .concurrency.asyncio import AsyncioBackend
from .concurrency.base import ConcurrencyBackend
from .config import (
DEFAULT_MAX_REDIRECTS,
DEFAULT_POOL_LIMITS,
Expand All @@ -17,6 +18,7 @@
VerifyTypes,
)
from .dispatch.asgi import ASGIDispatch
from .dispatch.base import AsyncDispatcher, Dispatcher
from .dispatch.connection_pool import ConnectionPool
from .dispatch.threaded import ThreadedDispatcher
from .dispatch.wsgi import WSGIDispatch
Expand All @@ -27,7 +29,6 @@
RedirectLoop,
TooManyRedirects,
)
from .interfaces import AsyncDispatcher, ConcurrencyBackend, Dispatcher
from .models import (
URL,
AsyncRequest,
Expand Down
39 changes: 4 additions & 35 deletions httpx/concurrency/asyncio.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,17 @@
import typing
from types import TracebackType

from ..config import PoolLimits, TimeoutConfig
from ..exceptions import ConnectTimeout, PoolTimeout, ReadTimeout, WriteTimeout
from ..interfaces import (
from .base import (
BaseBackgroundManager,
BasePoolSemaphore,
BaseQueue,
BaseReader,
BaseWriter,
ConcurrencyBackend,
TimeoutFlag,
)
from ..config import PoolLimits, TimeoutConfig
from ..exceptions import ConnectTimeout, PoolTimeout, ReadTimeout, WriteTimeout

SSL_MONKEY_PATCH_APPLIED = False

Expand All @@ -49,38 +50,6 @@ def _fixed_write(self, data: bytes) -> None: # type: ignore
MonkeyPatch.write = _fixed_write


class TimeoutFlag:
"""
A timeout flag holds a state of either read-timeout or write-timeout mode.
We use this so that we can attempt both reads and writes concurrently, while
only enforcing timeouts in one direction.
During a request/response cycle we start in write-timeout mode.
Once we've sent a request fully, or once we start seeing a response,
then we switch to read-timeout mode instead.
"""

def __init__(self) -> None:
self.raise_on_read_timeout = False
self.raise_on_write_timeout = True

def set_read_timeouts(self) -> None:
"""
Set the flag to read-timeout mode.
"""
self.raise_on_read_timeout = True
self.raise_on_write_timeout = False

def set_write_timeouts(self) -> None:
"""
Set the flag to write-timeout mode.
"""
self.raise_on_read_timeout = False
self.raise_on_write_timeout = True


class Reader(BaseReader):
def __init__(
self, stream_reader: asyncio.StreamReader, timeout: TimeoutConfig
Expand Down
127 changes: 26 additions & 101 deletions httpx/interfaces.py → httpx/concurrency/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,114 +2,39 @@
import typing
from types import TracebackType

from .config import CertTypes, PoolLimits, TimeoutConfig, TimeoutTypes, VerifyTypes
from .models import (
AsyncRequest,
AsyncRequestData,
AsyncResponse,
HeaderTypes,
QueryParamTypes,
Request,
RequestData,
Response,
URLTypes,
)


class AsyncDispatcher:
"""
Base class for async dispatcher classes, that handle sending the request.
Stubs out the interface, as well as providing a `.request()` convenience
implementation, to make it easy to use or test stand-alone dispatchers,
without requiring a complete `Client` instance.
"""

async def request(
self,
method: str,
url: URLTypes,
*,
data: AsyncRequestData = b"",
params: QueryParamTypes = None,
headers: HeaderTypes = None,
verify: VerifyTypes = None,
cert: CertTypes = None,
timeout: TimeoutTypes = None,
) -> AsyncResponse:
request = AsyncRequest(method, url, data=data, params=params, headers=headers)
return await self.send(request, verify=verify, cert=cert, timeout=timeout)

async def send(
self,
request: AsyncRequest,
verify: VerifyTypes = None,
cert: CertTypes = None,
timeout: TimeoutTypes = None,
) -> AsyncResponse:
raise NotImplementedError() # pragma: nocover

async def close(self) -> None:
pass # pragma: nocover

async def __aenter__(self) -> "AsyncDispatcher":
return self

async def __aexit__(
self,
exc_type: typing.Type[BaseException] = None,
exc_value: BaseException = None,
traceback: TracebackType = None,
) -> None:
await self.close()
from ..config import PoolLimits, TimeoutConfig


class Dispatcher:
class TimeoutFlag:
"""
Base class for synchronous dispatcher classes, that handle sending the request.
A timeout flag holds a state of either read-timeout or write-timeout mode.
Stubs out the interface, as well as providing a `.request()` convenience
implementation, to make it easy to use or test stand-alone dispatchers,
without requiring a complete `Client` instance.
"""

def request(
self,
method: str,
url: URLTypes,
*,
data: RequestData = b"",
params: QueryParamTypes = None,
headers: HeaderTypes = None,
verify: VerifyTypes = None,
cert: CertTypes = None,
timeout: TimeoutTypes = None,
) -> Response:
request = Request(method, url, data=data, params=params, headers=headers)
return self.send(request, verify=verify, cert=cert, timeout=timeout)

def send(
self,
request: Request,
verify: VerifyTypes = None,
cert: CertTypes = None,
timeout: TimeoutTypes = None,
) -> Response:
raise NotImplementedError() # pragma: nocover
We use this so that we can attempt both reads and writes concurrently, while
only enforcing timeouts in one direction.
def close(self) -> None:
pass # pragma: nocover
During a request/response cycle we start in write-timeout mode.
def __enter__(self) -> "Dispatcher":
return self
Once we've sent a request fully, or once we start seeing a response,
then we switch to read-timeout mode instead.
"""

def __exit__(
self,
exc_type: typing.Type[BaseException] = None,
exc_value: BaseException = None,
traceback: TracebackType = None,
) -> None:
self.close()
def __init__(self) -> None:
self.raise_on_read_timeout = False
self.raise_on_write_timeout = True

def set_read_timeouts(self) -> None:
"""
Set the flag to read-timeout mode.
"""
self.raise_on_read_timeout = True
self.raise_on_write_timeout = False

def set_write_timeouts(self) -> None:
"""
Set the flag to write-timeout mode.
"""
self.raise_on_read_timeout = False
self.raise_on_write_timeout = True


class BaseReader:
Expand Down
3 changes: 2 additions & 1 deletion httpx/dispatch/asgi.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import asyncio
import typing

from .base import AsyncDispatcher
from ..concurrency.base import ConcurrencyBackend
from ..concurrency.asyncio import AsyncioBackend
from ..config import CertTypes, TimeoutTypes, VerifyTypes
from ..interfaces import AsyncDispatcher, ConcurrencyBackend
from ..models import AsyncRequest, AsyncResponse


Expand Down
111 changes: 111 additions & 0 deletions httpx/dispatch/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import typing
from types import TracebackType

from ..config import CertTypes, TimeoutTypes, VerifyTypes
from ..models import (
AsyncRequest,
AsyncRequestData,
AsyncResponse,
HeaderTypes,
QueryParamTypes,
Request,
RequestData,
Response,
URLTypes,
)


class AsyncDispatcher:
"""
Base class for async dispatcher classes, that handle sending the request.
Stubs out the interface, as well as providing a `.request()` convenience
implementation, to make it easy to use or test stand-alone dispatchers,
without requiring a complete `Client` instance.
"""

async def request(
self,
method: str,
url: URLTypes,
*,
data: AsyncRequestData = b"",
params: QueryParamTypes = None,
headers: HeaderTypes = None,
verify: VerifyTypes = None,
cert: CertTypes = None,
timeout: TimeoutTypes = None,
) -> AsyncResponse:
request = AsyncRequest(method, url, data=data, params=params, headers=headers)
return await self.send(request, verify=verify, cert=cert, timeout=timeout)

async def send(
self,
request: AsyncRequest,
verify: VerifyTypes = None,
cert: CertTypes = None,
timeout: TimeoutTypes = None,
) -> AsyncResponse:
raise NotImplementedError() # pragma: nocover

async def close(self) -> None:
pass # pragma: nocover

async def __aenter__(self) -> "AsyncDispatcher":
return self

async def __aexit__(
self,
exc_type: typing.Type[BaseException] = None,
exc_value: BaseException = None,
traceback: TracebackType = None,
) -> None:
await self.close()


class Dispatcher:
"""
Base class for synchronous dispatcher classes, that handle sending the request.
Stubs out the interface, as well as providing a `.request()` convenience
implementation, to make it easy to use or test stand-alone dispatchers,
without requiring a complete `Client` instance.
"""

def request(
self,
method: str,
url: URLTypes,
*,
data: RequestData = b"",
params: QueryParamTypes = None,
headers: HeaderTypes = None,
verify: VerifyTypes = None,
cert: CertTypes = None,
timeout: TimeoutTypes = None,
) -> Response:
request = Request(method, url, data=data, params=params, headers=headers)
return self.send(request, verify=verify, cert=cert, timeout=timeout)

def send(
self,
request: Request,
verify: VerifyTypes = None,
cert: CertTypes = None,
timeout: TimeoutTypes = None,
) -> Response:
raise NotImplementedError() # pragma: nocover

def close(self) -> None:
pass # pragma: nocover

def __enter__(self) -> "Dispatcher":
return self

def __exit__(
self,
exc_type: typing.Type[BaseException] = None,
exc_value: BaseException = None,
traceback: TracebackType = None,
) -> None:
self.close()
Loading

0 comments on commit c0554e9

Please sign in to comment.