Skip to content

Commit 950a536

Browse files
authored
Merge pull request #14 from andy-takker/feature/update-docs
Add example for base_http_client
2 parents 225399d + 56dd7c5 commit 950a536

26 files changed

+431
-74
lines changed

.github/workflows/_publish.yaml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,9 @@ jobs:
77
publish-to-pypi:
88
name: Publish Python distribution to PyPI
99
runs-on: ubuntu-22.04
10-
# if: github.repository == 'andy-takker/base-http-client'
1110
environment:
1211
name: pypi
13-
url: https://pypi.org/p/base-http-client/
12+
url: https://pypi.org/p/asyncly/
1413
permissions:
1514
id-token: write
1615
contents: write

.pre-commit-config.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ default_language_version:
33

44
repos:
55
- repo: https://github.com/pre-commit/pre-commit-hooks
6-
rev: v4.6.0
6+
rev: v5.0.0
77
hooks:
88
- id: check-added-large-files
99
- id: check-ast
@@ -23,7 +23,7 @@ repos:
2323
- id: trailing-whitespace
2424

2525
- repo: https://github.com/astral-sh/ruff-pre-commit
26-
rev: v0.4.10
26+
rev: v0.7.2
2727
hooks:
2828
- id: ruff
2929
args: [--fix]
@@ -38,7 +38,7 @@ repos:
3838
hooks:
3939
- id: mypy
4040
name: mypy
41-
entry: mypy ./base_http_client --config-file ./pyproject.toml
41+
entry: mypy ./asyncly --config-file ./pyproject.toml
4242
language: python
4343
language_version: python3.10
4444
require_serial: true

Makefile

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
PROJECT_NAME = base_http_client
1+
PROJECT_NAME = asyncly
22

3-
develop:
3+
develop: clean_dev
44
python3.10 -m venv .venv
55
.venv/bin/pip install -U pip poetry
66
.venv/bin/poetry config virtualenvs.create false
@@ -21,4 +21,7 @@ mypy-ci: ##@Linting Run mypy
2121
mypy ./$(PROJECT_NAME) --config-file ./pyproject.toml
2222

2323
rst-ci: ##@Linting Run rst-lint
24-
rst-lint --encoding utf-8 README.rst
24+
rst-lint --encoding utf-8 README.rst
25+
26+
clean_dev:
27+
rm -rf .venv

README.rst

Lines changed: 124 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
1-
Base HTTP Client
2-
================
1+
Asyncly
2+
=======
33

4-
.. image:: https://img.shields.io/pypi/v/base-http-client.svg
5-
:target: https://pypi.python.org/pypi/base-http-client/
4+
.. image:: https://img.shields.io/pypi/v/asyncly.svg
5+
:target: https://pypi.python.org/pypi/asyncly/
66
:alt: Latest Version
77

8-
.. image:: https://img.shields.io/pypi/wheel/base-http-client.svg
9-
:target: https://pypi.python.org/pypi/base-http-client/
8+
.. image:: https://img.shields.io/pypi/wheel/asyncly.svg
9+
:target: https://pypi.python.org/pypi/asyncly/
1010

11-
.. image:: https://img.shields.io/pypi/pyversions/base-http-client.svg
12-
:target: https://pypi.python.org/pypi/base-http-client/
11+
.. image:: https://img.shields.io/pypi/pyversions/asyncly.svg
12+
:target: https://pypi.python.org/pypi/asyncly/
1313

14-
.. image:: https://img.shields.io/pypi/l/base-http-client.svg
15-
:target: https://pypi.python.org/pypi/base-http-client/
14+
.. image:: https://img.shields.io/pypi/l/asyncly.svg
15+
:target: https://pypi.python.org/pypi/asyncly/
1616

17-
Base HTTP client for your integrations based on aiohttp_.
17+
Simple HTTP client and server for your integrations based on aiohttp_.
1818

1919
Installation
2020
------------
@@ -26,13 +26,13 @@ Installing from PyPI_:
2626

2727
.. code-block:: bash
2828
29-
pip3 install base-http-client
29+
pip install asyncly
3030
3131
Installing from github.com:
3232

3333
.. code-block:: bash
3434
35-
pip3 install git+https://github.com/andy-takker/base_http_client
35+
pip install git+https://github.com/andy-takker/asyncly
3636
3737
The package contains several extras and you can install additional dependencies
3838
if you specify them in this way.
@@ -41,28 +41,123 @@ For example, with msgspec_:
4141

4242
.. code-block:: bash
4343
44-
pip3 install "base-http-client[msgspec]"
44+
pip install "asyncly[msgspec]"
4545
4646
Complete table of extras below:
4747

48-
+-----------------------------------------------+----------------------------------+
49-
| example | description |
50-
+===============================================+==================================+
51-
| ``pip3 install "base-http-client[msgspec]"`` | For using msgspec_ structs |
52-
+-----------------------------------------------+----------------------------------+
53-
| ``pip3 install "base-http-client[orjson]"`` | For fast parsing json by orjson_ |
54-
+-----------------------------------------------+----------------------------------+
55-
| ``pip3 install "base-http-client[pydantic]"`` | For using pydantic_ models |
56-
+-----------------------------------------------+----------------------------------+
57-
58-
Using
59-
~~~~~
60-
61-
48+
+-------------------------------------+----------------------------------+
49+
| example | description |
50+
+========================================================================+
51+
| ``pip install "asyncly[msgspec]"`` | For using msgspec_ structs |
52+
+-------------------------------------+----------------------------------+
53+
| ``pip install "asyncly[orjson]"`` | For fast parsing json by orjson_ |
54+
+-------------------------------------+----------------------------------+
55+
| ``pip install "asyncly[pydantic]"`` | For using pydantic_ models |
56+
+-------------------------------------+----------------------------------+
57+
58+
Quick start guide
59+
-----------------
60+
61+
HttpClient
62+
~~~~~~~~~~~~~~
63+
64+
Simple HTTP Client for `https://catfact.ninja`. See full example in `examples/catfact_client.py`_
65+
66+
.. code-block:: python
67+
68+
from asyncly import DEFAULT_TIMEOUT, BaseHttpClient, ResponseHandlersType
69+
from asyncly.client.handlers.pydantic import parse_model
70+
from asyncly.client.timeout import TimeoutType
71+
72+
73+
class CatfactClient(BaseHttpClient):
74+
RANDOM_CATFACT_HANDLERS: ResponseHandlersType = MappingProxyType(
75+
{
76+
HTTPStatus.OK: parse_model(CatfactSchema),
77+
}
78+
)
79+
80+
async def fetch_random_cat_fact(
81+
self,
82+
timeout: TimeoutType = DEFAULT_TIMEOUT,
83+
) -> CatfactSchema:
84+
return await self._make_req(
85+
method=hdrs.METH_GET,
86+
url=self._url / "fact",
87+
handlers=self.RANDOM_CATFACT_HANDLERS,
88+
timeout=timeout,
89+
)
90+
91+
Test Async Server for client
92+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
93+
94+
For the HTTP client, we create a server to which he will go and simulate real
95+
responses. You can dynamically change the responses from the server in
96+
a specific test.
97+
98+
Let's prepare the fixtures:
99+
100+
.. code-block:: python
101+
102+
@pytest.fixture
103+
async def catafact_service() -> AsyncIterator[MockService]:
104+
routes = [
105+
MockRoute("GET", "/fact", "random_catfact"),
106+
]
107+
async with start_service(routes) as service:
108+
service.register(
109+
"random_catfact",
110+
JsonResponse({"fact": "test", "length": 4}),
111+
)
112+
yield service
113+
114+
115+
@pytest.fixture
116+
def catfact_url(catafact_service: MockService) -> URL:
117+
return catafact_service.url
118+
119+
120+
@pytest.fixture
121+
async def catfact_client(catfact_url: URL) -> AsyncIterator[CatfactClient]:
122+
async with ClientSession() as session:
123+
client = CatfactClient(
124+
client_name="catfact",
125+
session=session,
126+
url=catfact_url,
127+
)
128+
yield client
129+
130+
Now we can use them in tests. See full example in `examples/test_catfact_client.py`_
131+
132+
.. code-block:: python
133+
134+
async def test_fetch_random_catfact(catfact_client: CatfactClient) -> None:
135+
# use default registered handler
136+
fact = await catfact_client.fetch_random_cat_fact()
137+
assert fact == CatfactSchema(fact="test", length=4)
138+
139+
140+
async def test_fetch_random_catfact_timeout(
141+
catfact_client: CatfactClient,
142+
catafact_service: MockService,
143+
) -> None:
144+
# change default registered handler to time error handler
145+
catafact_service.register(
146+
"random_catfact",
147+
LatencyResponse(
148+
wrapped=JsonResponse({"fact": "test", "length": 4}),
149+
latency=1.5,
150+
),
151+
)
152+
with pytest.raises(asyncio.TimeoutError):
153+
await catfact_client.fetch_random_cat_fact(timeout=1)
62154
63155
64156
.. _PyPI: https://pypi.org/
65157
.. _aiohttp: https://pypi.org/project/aiohttp/
66158
.. _msgspec: https://github.com/jcrist/msgspec
67159
.. _orjson: https://github.com/ijl/orjson
68-
.. _pydantic: https://github.com/pydantic/pydantic
160+
.. _pydantic: https://github.com/pydantic/pydantic
161+
162+
.. _examples/catfact_client.py: https://github.com/andy-takker/asyncly/blob/master/examples/catfact_client.py
163+
.. _examples/test_catfact_client.py: https://github.com/andy-takker/asyncly/blob/master/examples/test_catfact_client.py

asyncly/__init__.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from aiohttp.client import DEFAULT_TIMEOUT
2+
3+
from asyncly.client.base import BaseHttpClient
4+
from asyncly.client.handlers.base import ResponseHandlersType
5+
from asyncly.client.timeout import TimeoutType
6+
7+
__all__ = (
8+
"BaseHttpClient",
9+
"TimeoutType",
10+
"ResponseHandlersType",
11+
"DEFAULT_TIMEOUT",
12+
)
Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@
44
from aiohttp.client import DEFAULT_TIMEOUT
55
from yarl import URL
66

7-
from base_http_client.handlers.base import (
7+
from asyncly.client.handlers.base import (
88
ResponseHandlersType,
99
apply_handler,
1010
)
11-
from .timeout import TimeoutType, get_timeout
11+
from asyncly.client.timeout import TimeoutType, get_timeout
1212

1313

1414
class BaseHttpClient:
@@ -18,8 +18,10 @@ class BaseHttpClient:
1818
_session: ClientSession
1919
_client_name: str
2020

21-
def __init__(self, url: URL, session: ClientSession, client_name: str) -> None:
22-
self._url = url
21+
def __init__(
22+
self, url: URL | str, session: ClientSession, client_name: str
23+
) -> None:
24+
self._url = url if isinstance(url, URL) else URL(url)
2325
self._session = session
2426
self._client_name = client_name
2527

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
from aiohttp import ClientResponse
66

7-
from base_http_client.exceptions import UnhandledStatusException
7+
from asyncly.client.handlers.exceptions import UnhandledStatusException
88

99
ResponseHandlersType = Mapping[HTTPStatus | int | str, Callable]
1010

0 commit comments

Comments
 (0)