From: Tom Christie Date: Mon, 20 May 2019 13:30:52 +0000 (+0100) Subject: Add top-level API (#75) X-Git-Tag: 0.3.1~11 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=fc627f33875d2572c5953c0fb4b32348cf9fddf4;p=thirdparty%2Fhttpx.git Add top-level API (#75) * Add top-level API * Add tests for top-level API --- diff --git a/README.md b/README.md index d5333ba7..eda4162f 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ Plus all the standard features of requests... * Automatic Decompression * Automatic Content Decoding * Unicode Response Bodies -* Multipart File Uploads *TODO - Request content currently supports URL encoded data, bytes, or async byte iterators.* +* Multipart File Uploads *TODO - Request content currently supports URL encoded data, JSON, bytes, or async byte iterators.* * HTTP(S) Proxy Support *TODO* * Connection Timeouts * Streaming Downloads diff --git a/httpcore/__init__.py b/httpcore/__init__.py index fddb2cff..6d83e16f 100644 --- a/httpcore/__init__.py +++ b/httpcore/__init__.py @@ -1,3 +1,4 @@ +from .api import delete, get, head, options, patch, post, put, request from .client import AsyncClient, Client from .concurrency import AsyncioBackend from .config import PoolLimits, SSLConfig, TimeoutConfig diff --git a/httpcore/api.py b/httpcore/api.py new file mode 100644 index 00000000..8e242c74 --- /dev/null +++ b/httpcore/api.py @@ -0,0 +1,236 @@ +import typing + +from .client import Client +from .config import SSLConfig, TimeoutConfig +from .models import ( + AuthTypes, + CookieTypes, + HeaderTypes, + QueryParamTypes, + RequestData, + SyncResponse, + URLTypes, +) + + +def request( + method: str, + url: URLTypes, + *, + data: RequestData = b"", + json: typing.Any = None, + params: QueryParamTypes = None, + headers: HeaderTypes = None, + cookies: CookieTypes = None, + stream: bool = False, + auth: AuthTypes = None, + allow_redirects: bool = True, + ssl: SSLConfig = None, + timeout: TimeoutConfig = None, +) -> SyncResponse: + with Client() as client: + return client.request( + method=method, + url=url, + data=data, + json=json, + params=params, + headers=headers, + cookies=cookies, + stream=stream, + auth=auth, + allow_redirects=allow_redirects, + ssl=ssl, + timeout=timeout, + ) + + +def get( + url: URLTypes, + *, + params: QueryParamTypes = None, + headers: HeaderTypes = None, + cookies: CookieTypes = None, + stream: bool = False, + auth: AuthTypes = None, + allow_redirects: bool = True, + ssl: SSLConfig = None, + timeout: TimeoutConfig = None, +) -> SyncResponse: + return request( + "GET", + url, + headers=headers, + cookies=cookies, + stream=stream, + auth=auth, + allow_redirects=allow_redirects, + ssl=ssl, + timeout=timeout, + ) + + +def options( + url: URLTypes, + *, + params: QueryParamTypes = None, + headers: HeaderTypes = None, + cookies: CookieTypes = None, + stream: bool = False, + auth: AuthTypes = None, + allow_redirects: bool = True, + ssl: SSLConfig = None, + timeout: TimeoutConfig = None, +) -> SyncResponse: + return request( + "OPTIONS", + url, + headers=headers, + cookies=cookies, + stream=stream, + auth=auth, + allow_redirects=allow_redirects, + ssl=ssl, + timeout=timeout, + ) + + +def head( + url: URLTypes, + *, + params: QueryParamTypes = None, + headers: HeaderTypes = None, + cookies: CookieTypes = None, + stream: bool = False, + auth: AuthTypes = None, + allow_redirects: bool = False, #  Note: Differs to usual default. + ssl: SSLConfig = None, + timeout: TimeoutConfig = None, +) -> SyncResponse: + return request( + "HEAD", + url, + headers=headers, + cookies=cookies, + stream=stream, + auth=auth, + allow_redirects=allow_redirects, + ssl=ssl, + timeout=timeout, + ) + + +def post( + url: URLTypes, + *, + data: RequestData = b"", + json: typing.Any = None, + params: QueryParamTypes = None, + headers: HeaderTypes = None, + cookies: CookieTypes = None, + stream: bool = False, + auth: AuthTypes = None, + allow_redirects: bool = True, + ssl: SSLConfig = None, + timeout: TimeoutConfig = None, +) -> SyncResponse: + return request( + "POST", + url, + data=data, + json=json, + headers=headers, + cookies=cookies, + stream=stream, + auth=auth, + allow_redirects=allow_redirects, + ssl=ssl, + timeout=timeout, + ) + + +def put( + url: URLTypes, + *, + data: RequestData = b"", + json: typing.Any = None, + params: QueryParamTypes = None, + headers: HeaderTypes = None, + cookies: CookieTypes = None, + stream: bool = False, + auth: AuthTypes = None, + allow_redirects: bool = True, + ssl: SSLConfig = None, + timeout: TimeoutConfig = None, +) -> SyncResponse: + return request( + "PUT", + url, + data=data, + json=json, + headers=headers, + cookies=cookies, + stream=stream, + auth=auth, + allow_redirects=allow_redirects, + ssl=ssl, + timeout=timeout, + ) + + +def patch( + url: URLTypes, + *, + data: RequestData = b"", + json: typing.Any = None, + params: QueryParamTypes = None, + headers: HeaderTypes = None, + cookies: CookieTypes = None, + stream: bool = False, + auth: AuthTypes = None, + allow_redirects: bool = True, + ssl: SSLConfig = None, + timeout: TimeoutConfig = None, +) -> SyncResponse: + return request( + "PATCH", + url, + data=data, + json=json, + headers=headers, + cookies=cookies, + stream=stream, + auth=auth, + allow_redirects=allow_redirects, + ssl=ssl, + timeout=timeout, + ) + + +def delete( + url: URLTypes, + *, + data: RequestData = b"", + json: typing.Any = None, + params: QueryParamTypes = None, + headers: HeaderTypes = None, + cookies: CookieTypes = None, + stream: bool = False, + auth: AuthTypes = None, + allow_redirects: bool = True, + ssl: SSLConfig = None, + timeout: TimeoutConfig = None, +) -> SyncResponse: + return request( + "DELETE", + url, + data=data, + json=json, + headers=headers, + cookies=cookies, + stream=stream, + auth=auth, + allow_redirects=allow_redirects, + ssl=ssl, + timeout=timeout, + ) diff --git a/tests/test_api.py b/tests/test_api.py new file mode 100644 index 00000000..6a62359c --- /dev/null +++ b/tests/test_api.py @@ -0,0 +1,73 @@ +import asyncio +import functools + +import pytest + +import httpcore + + +def threadpool(func): + """ + Our sync tests should run in seperate thread to the uvicorn server. + """ + + @functools.wraps(func) + async def wrapped(*args, **kwargs): + nonlocal func + + loop = asyncio.get_event_loop() + if kwargs: + func = functools.partial(func, **kwargs) + await loop.run_in_executor(None, func, *args) + + return pytest.mark.asyncio(wrapped) + + +@threadpool +def test_get(server): + response = httpcore.get("http://127.0.0.1:8000/") + assert response.status_code == 200 + assert response.reason_phrase == "OK" + assert response.text == "Hello, world!" + + +@threadpool +def test_post(server): + response = httpcore.post("http://127.0.0.1:8000/", data=b"Hello, world!") + assert response.status_code == 200 + assert response.reason_phrase == "OK" + + +@threadpool +def test_options(server): + response = httpcore.options("http://127.0.0.1:8000/") + assert response.status_code == 200 + assert response.reason_phrase == "OK" + + +@threadpool +def test_head(server): + response = httpcore.head("http://127.0.0.1:8000/") + assert response.status_code == 200 + assert response.reason_phrase == "OK" + + +@threadpool +def test_put(server): + response = httpcore.put("http://127.0.0.1:8000/", data=b"Hello, world!") + assert response.status_code == 200 + assert response.reason_phrase == "OK" + + +@threadpool +def test_patch(server): + response = httpcore.patch("http://127.0.0.1:8000/", data=b"Hello, world!") + assert response.status_code == 200 + assert response.reason_phrase == "OK" + + +@threadpool +def test_delete(server): + response = httpcore.delete("http://127.0.0.1:8000/") + assert response.status_code == 200 + assert response.reason_phrase == "OK"