Skip to content

Commit 03d6e7f

Browse files
authored
Use httpx (#24)
* Use httpx * Drop 3.10 * Fix lock
1 parent be1ccdc commit 03d6e7f

File tree

8 files changed

+339
-697
lines changed

8 files changed

+339
-697
lines changed

.github/workflows/build.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,12 @@ jobs:
1616
PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }}
1717
strategy:
1818
matrix:
19-
python-version: ["3.10", "3.11", "3.12"]
19+
python-version: ["3.11", "3.12", "3.13"]
2020

2121
steps:
2222
- uses: actions/checkout@v3
2323
- name: Set up Python ${{ matrix.python-version }}
24-
uses: actions/setup-python@v3
24+
uses: actions/setup-python@v5
2525
with:
2626
python-version: ${{ matrix.python-version }}
2727
- name: Install dependencies
@@ -31,11 +31,11 @@ jobs:
3131
- name: run tests
3232
run: make test
3333
- name: upload coverage
34-
if: matrix.python-version == '3.12'
35-
uses: codecov/codecov-action@v3
34+
if: matrix.python-version == '3.13'
35+
uses: codecov/codecov-action@v4
3636
with:
3737
token: ${{ secrets.CODECOV_TOKEN }}
3838
files: ./build/coverage.xml
3939
- name: release
40-
if: ${{matrix.python-version == '3.12' && github.ref == 'refs/heads/main' && github.event.head_commit.message == 'release'}}
40+
if: ${{matrix.python-version == '3.13' && github.ref == 'refs/heads/main' && github.event.head_commit.message == 'release'}}
4141
run: make publish

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Copyright (c) 2020 Quantmind
1+
Copyright (c) 2025 Quantmind
22

33
Redistribution and use in source and binary forms, with or without modification,
44
are permitted provided that the following conditions are met:

metablock/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from .spaces import Block, Service, Space, SpaceExtension
66
from .user import User
77

8-
__version__ = "0.7.1"
8+
__version__ = "0.9.0"
99

1010
__all__ = [
1111
"Metablock",

metablock/client.py

Lines changed: 21 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33
import logging
44
import os
55
import sys
6-
from typing import Any
6+
from typing import Any, Self
77

8-
from aiohttp import ClientResponse, ClientSession
9-
from yarl import URL
8+
from httpx import AsyncClient
9+
from httpx import Response as ClientResponse
1010

1111
from .components import Callback, HttpComponent, MetablockResponseError
1212
from .extensions import Extension, Extensions, Plugin, Plugins
@@ -30,13 +30,14 @@ def __init__(
3030
url: str | None = None,
3131
auth_key: str = "",
3232
auth_key_name: str = "x-metablock-api-key",
33-
session: ClientSession | None = None,
33+
session: AsyncClient | None = None,
3434
user_agent: str = DEFAULT_USER_AGENT,
3535
) -> None:
3636
self.url: str = url if url is not None else self.url
3737
self.auth_key: str = auth_key or self.auth_key
3838
self.auth_key_name = auth_key_name
3939
self.session = session
40+
self.session_owner = session is None
4041
self.default_headers: dict[str, str] = {
4142
"user-agent": user_agent,
4243
"accept": "application/json",
@@ -49,18 +50,14 @@ def __init__(
4950
self.domains = Domains(self)
5051
self.services = self.blocks
5152

52-
def __repr__(self) -> str:
53-
return self.url
54-
55-
__str__ = __repr__
56-
5753
@property
58-
def cli(self) -> Metablock:
54+
def cli(self) -> Self:
5955
return self
6056

6157
async def close(self) -> None:
62-
if self.session:
63-
await self.session.close()
58+
if self.session and self.session_owner:
59+
await self.session.aclose()
60+
self.session = None
6461

6562
async def __aenter__(self) -> Metablock:
6663
return self
@@ -71,37 +68,37 @@ async def __aexit__(self, exc_type: type, exc_val: Any, exc_tb: Any) -> None:
7168
async def spec(self) -> dict:
7269
return await self.request(f"{self.url}/spec")
7370

74-
async def get(self, url: str | URL, **kwargs: Any) -> Any:
71+
async def get(self, url: str, **kwargs: Any) -> Any:
7572
kwargs["method"] = "GET"
7673
return await self.request(url, **kwargs)
7774

78-
async def patch(self, url: str | URL, **kwargs: Any) -> Any:
75+
async def patch(self, url: str, **kwargs: Any) -> Any:
7976
kwargs["method"] = "PATCH"
8077
return await self.request(url, **kwargs)
8178

82-
async def post(self, url: str | URL, **kwargs: Any) -> Any:
79+
async def post(self, url: str, **kwargs: Any) -> Any:
8380
kwargs["method"] = "POST"
8481
return await self.request(url, **kwargs)
8582

86-
async def put(self, url: str | URL, **kwargs: Any) -> Any:
83+
async def put(self, url: str, **kwargs: Any) -> Any:
8784
kwargs["method"] = "PUT"
8885
return await self.request(url, **kwargs)
8986

90-
async def delete(self, url: str | URL, **kwargs: Any) -> Any:
87+
async def delete(self, url: str, **kwargs: Any) -> Any:
9188
kwargs["method"] = "DELETE"
9289
return await self.request(url, **kwargs)
9390

9491
async def request(
9592
self,
96-
url: str | URL,
93+
url: str,
9794
method: str = "",
9895
headers: dict[str, str] | None = None,
9996
callback: Callback | None = None,
10097
wrap: Any = None,
10198
**kw: Any,
10299
) -> Any:
103100
if not self.session:
104-
self.session = ClientSession()
101+
self.session = AsyncClient()
105102
method = method or "GET"
106103
headers_ = self.get_default_headers()
107104
headers_.update(headers or ())
@@ -112,16 +109,16 @@ async def request(
112109
return await self.handle_response(response, wrap=wrap)
113110

114111
async def handle_response(self, response: ClientResponse, wrap: Any = None) -> Any:
115-
if response.status == 204:
112+
if response.status_code == 204:
116113
return True
117-
if response.status >= 400:
114+
if response.status_code >= 400:
118115
try:
119-
data = await response.json()
116+
data = response.json()
120117
except Exception:
121-
data = await response.text()
118+
data = response.text
122119
raise MetablockResponseError(response, data)
123120
response.raise_for_status()
124-
data = await response.json()
121+
data = response.json()
125122
return wrap(data) if wrap else data
126123

127124
async def get_user(self, **kw: Any) -> User:

metablock/components.py

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from __future__ import annotations
22

33
import json
4-
from abc import ABC, abstractproperty
4+
from abc import ABC, abstractmethod
55
from typing import (
66
TYPE_CHECKING,
77
Any,
@@ -14,7 +14,7 @@
1414
cast,
1515
)
1616

17-
from aiohttp import ClientResponse
17+
from httpx import Response as ClientResponse
1818

1919
from .utils import as_dict, as_params
2020

@@ -34,23 +34,25 @@ def __init__(self, response: ClientResponse, message: Any = "") -> None:
3434
self.response = response
3535
self.message = as_dict(message, "message")
3636
self.message["request_url"] = str(response.url)
37-
self.message["request_method"] = response.method
38-
self.message["response_status"] = response.status
37+
self.message["request_method"] = response.request.method
38+
self.message["response_status"] = response.status_code
3939

4040
@property
4141
def status(self) -> int:
42-
return self.response.status
42+
return self.response.status_code
4343

4444
def __str__(self) -> str:
4545
return json.dumps(self.message, indent=4)
4646

4747

4848
class HttpComponent(ABC):
49-
@abstractproperty
49+
@property
50+
@abstractmethod
5051
def cli(self) -> Metablock: # pragma: no cover
5152
...
5253

53-
@abstractproperty
54+
@property
55+
@abstractmethod
5456
def url(self) -> str: # pragma: no cover
5557
...
5658

@@ -237,9 +239,9 @@ def delete_url(self, id_name: str) -> str:
237239
# callbacks
238240

239241
async def _head(self, response: ClientResponse) -> bool:
240-
if response.status == 404:
242+
if response.status_code == 404:
241243
return False
242-
elif response.status == 200:
244+
elif response.status_code == 200:
243245
return True
244246
else: # pragma: no cover
245247
raise MetablockResponseError(response)

metablock/spaces.py

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22

33
from typing import Any
44

5-
from aiohttp.formdata import FormData
6-
75
from .components import Component, CrudComponent, MetablockEntity
86

97

@@ -50,12 +48,11 @@ async def certificate(self, *, callback: Any = None) -> dict:
5048
async def ship(
5149
self, name: str, bundle: str, env: str = "stage", *, callback: Any = None
5250
) -> dict:
53-
data = FormData()
54-
data.add_field("name", name)
55-
data.add_field("bundle", open(bundle, "rb"), filename=bundle)
56-
data.add_field("env", env)
5751
return await self.cli.post(
58-
f"{self.url}/deployments", data=data, callback=callback
52+
f"{self.url}/deployments",
53+
data=dict(name=name, env=env),
54+
files=dict(bundle=(bundle, open(bundle, "rb"))),
55+
callback=callback,
5956
)
6057

6158
async def add_route(self, *, callback: Any = None, **kwargs: Any) -> dict:

0 commit comments

Comments
 (0)