Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 14 additions & 20 deletions src/hip/__init__.py
Original file line number Diff line number Diff line change
@@ -1,43 +1,37 @@
from ._client import * # Client

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Happy to say goodbye to these import * lines.

from ._connection import * # Connection, Transport, ResponseContext
from ._content import * # Binary, Content, File, Files, Form, HTML, JSON, MultiPart, Text
from ._headers import * # Headers
from ._network import * # NetworkBackend, NetworkStream, timeout
from ._parsers import * # HTTPParser, ProtocolError
from ._pool import * # ConnectionPool
from ._request import * # Method, Request
from ._response import * # StatusCode, Response
from ._server import * # Server
from ._streams import * # Stream
from ._urls import * # QueryParams, URL
from . import backends, parsers, transports
from ._client import Client
from ._content import HTML, JSON, Binary, Content, File, Files, Form, MultiPart, Text
from ._headers import Headers
from ._request import Method, Request
from ._response import Response, StatusCode
from ._server import Server
from ._streams import Stream
from ._timeouts import sleep, timeout
from ._urls import URL, QueryParams

__all__ = [
"HTML",
"JSON",
"URL",
"Binary",
"Client",
"Connection",
"ConnectionPool",
"Content",
"File",
"Files",
"Form",
"HTTPParser",
"Headers",
"Method",
"MultiPart",
"NetworkBackend",
"NetworkStream",
"ProtocolError",
"QueryParams",
"Request",
"Response",
"Server",
"StatusCode",
"Stream",
"Text",
"Transport",
"open_connection",
"backends",
"parsers",
"sleep",
"timeout",
"transports",
]
1 change: 1 addition & 0 deletions src/hip/_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

__all__ = [
"Connection",
"ResponseStream",
"Transport",
"open_connection",
]
Expand Down
41 changes: 2 additions & 39 deletions src/hip/_network.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
import concurrent.futures
import contextlib
import contextvars
import select
import socket
import ssl
import threading
import time
import types
import typing

Expand All @@ -16,42 +13,9 @@
from typing_extensions import Self

from ._streams import Stream
from ._timeouts import get_current_timeout

__all__ = ["NetworkBackend", "NetworkStream", "timeout"]

_timeout_stack: contextvars.ContextVar[tuple[float, ...]] = contextvars.ContextVar(
"timeout_context", default=()
)


@contextlib.contextmanager
def timeout(duration: float) -> typing.Iterator[None]:
"""
A context managed timeout API.

with timeout(1.0):
...
"""
now = time.monotonic()
until = now + duration
stack = (until, *_timeout_stack.get())
token = _timeout_stack.set(stack)
try:
yield
finally:
_timeout_stack.reset(token)


def get_current_timeout() -> float | None:
stack = _timeout_stack.get()
if not stack:
return None
soonest = min(stack)
now = time.monotonic()
remaining = soonest - now
if remaining <= 0.0:
raise TimeoutError()
return remaining
__all__ = ["NetworkBackend", "NetworkStream"]


class NetworkStream(Stream):
Expand Down Expand Up @@ -249,4 +213,3 @@ def __repr__(self):

Semaphore = threading.Semaphore
Lock = threading.Lock
sleep = time.sleep
3 changes: 2 additions & 1 deletion src/hip/_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@
from ._body import HTTPStream
from ._content import Text
from ._headers import Headers
from ._network import NetworkBackend, sleep
from ._network import NetworkBackend
from ._parsers import HTTPParser
from ._request import Request
from ._response import Response
from ._timeouts import sleep

__all__ = ["Server"]

Expand Down
52 changes: 52 additions & 0 deletions src/hip/_timeouts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import contextlib
import contextvars
import time
import typing

__all__ = ["sleep", "timeout"]


_timeout_stack: contextvars.ContextVar[tuple[float, ...]] = contextvars.ContextVar(
"timeout_context", default=()
)


@contextlib.contextmanager
def timeout(duration: float) -> typing.Iterator[None]:
"""
A context managed timeout API.

with timeout(1.0):
...
"""
now = time.monotonic()
until = now + duration
stack = (until, *_timeout_stack.get())
token = _timeout_stack.set(stack)
try:
yield
finally:
_timeout_stack.reset(token)


def get_current_timeout() -> float | None:
stack = _timeout_stack.get()
if not stack:
return None
soonest = min(stack)
now = time.monotonic()
remaining = soonest - now
if remaining <= 0.0:
raise TimeoutError()
return remaining


def sleep(dur: float):
"""
A `sleep()` implementation that is aware of the current timeout context.
"""
remaining = get_current_timeout()
if remaining is not None and remaining <= dur: # pragma: nocover
time.sleep(remaining)
raise TimeoutError()
time.sleep(dur)
6 changes: 6 additions & 0 deletions src/hip/backends.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from ._network import NetworkBackend, NetworkStream

__all__ = [
"NetworkBackend",
"NetworkStream",
]
6 changes: 6 additions & 0 deletions src/hip/parsers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from ._parsers import HTTPParser, ProtocolError

__all__ = [
"HTTPParser",
"ProtocolError",
]
10 changes: 10 additions & 0 deletions src/hip/transports.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from ._connection import Connection, ResponseStream, Transport, open_connection
from ._pool import ConnectionPool

__all__ = [
"Connection",
"ConnectionPool",
"ResponseStream",
"Transport",
"open_connection",
]
16 changes: 8 additions & 8 deletions tests/test_network.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,18 @@ def echo(stream):

@pytest.fixture
def server():
net = hip.NetworkBackend()
net = hip.backends.NetworkBackend()
with net.serve("127.0.0.1", 8080, echo) as server:
yield server


def test_network_backend():
net = hip.NetworkBackend()
net = hip.backends.NetworkBackend()
assert repr(net) in ["<NetworkBackend [trio]>", "<NetworkBackend [threaded]>"]


def test_network_backend_connect(server):
net = hip.NetworkBackend()
net = hip.backends.NetworkBackend()
stream = net.connect(server.host, server.port)
try:
assert repr(stream) == f"<NetworkStream [{server.host}:{server.port}]>"
Expand Down Expand Up @@ -61,7 +61,7 @@ def shutdown(self, how):

def test_network_stream_close_ignores_shutdown_errors():
sock = FailingShutdownSocket()
stream = hip.NetworkStream(sock, ("example.com", 443))
stream = hip.backends.NetworkStream(sock, ("example.com", 443))

stream.close()

Expand All @@ -72,7 +72,7 @@ def test_network_backend_connect_tls(monkeypatch):
raw_socket = FakeSocket()
tls_socket = FakeSocket()
ssl_context = FakeSSLContext(tls_socket)
backend = hip.NetworkBackend(ssl_ctx=ssl_context)
backend = hip.backends.NetworkBackend(ssl_ctx=ssl_context)

create_connection_args = []

Expand All @@ -92,7 +92,7 @@ def create_connection(address, timeout):


def test_network_listener_context_managed():
backend = hip.NetworkBackend()
backend = hip.backends.NetworkBackend()

with backend.listen("127.0.0.1", 0) as listener:
assert listener.host == "127.0.0.1"
Expand All @@ -101,7 +101,7 @@ def test_network_listener_context_managed():


def test_network_backend_context_managed(server):
net = hip.NetworkBackend()
net = hip.backends.NetworkBackend()
with net.connect(server.host, server.port) as stream:
stream.write(b"Hello, world.")
content = stream.read()
Expand All @@ -110,7 +110,7 @@ def test_network_backend_context_managed(server):


def test_network_backend_timeout(server):
net = hip.NetworkBackend()
net = hip.backends.NetworkBackend()
with (
hip.timeout(0.0),
pytest.raises(TimeoutError),
Expand Down
Loading
Loading