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
6 changes: 3 additions & 3 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }}
strategy:
matrix:
python-version: ["3.11", "3.12", "3.13"]
python-version: ["3.11", "3.12", "3.13", "3.14"]

steps:
- uses: actions/checkout@v3
Expand All @@ -31,11 +31,11 @@ jobs:
- name: run tests
run: make test
- name: upload coverage
if: matrix.python-version == '3.13'
if: matrix.python-version == '3.14'
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./build/coverage.xml
- name: release
if: ${{matrix.python-version == '3.13' && github.ref == 'refs/heads/main' && github.event.head_commit.message == 'release'}}
if: ${{matrix.python-version == '3.14' && github.ref == 'refs/heads/main' && github.event.head_commit.message == 'release'}}
run: make publish
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Copyright (c) 2025 Quantmind
Copyright (c) 2026 Quantmind

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
Expand Down
8 changes: 3 additions & 5 deletions metablock/__init__.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,20 @@
from .client import Metablock
from .components import MetablockEntity, MetablockError, MetablockResponseError
from .extensions import Extension, Plugin
from .extensions import Extension
from .orgs import Org
from .spaces import Block, Service, Space, SpaceExtension
from .spaces import Block, Space, SpaceExtension
from .user import User

__version__ = "1.1.0"
__version__ = "1.2.0"

__all__ = [
"Metablock",
"MetablockError",
"MetablockResponseError",
"MetablockEntity",
"Space",
"Service",
"Block",
"Extension",
"Plugin",
"SpaceExtension",
"Org",
"User",
Expand Down
6 changes: 3 additions & 3 deletions metablock/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,9 @@ async def _apply(path: str, space_name: str, token: str, dry_run: bool) -> None:
raise click.Abort()
async with Metablock(auth_key=token) as mb:
space = await mb.spaces.get(space_name)
svc = await space.blocks.get_list()
click.echo(f"space {space.name} has {len(svc)} blocks")
by_name = {s["name"]: s for s in svc}
space_blocks = await space.blocks.get_list()
click.echo(f"space {space.name} has {len(space_blocks)} blocks")
by_name = {s.name: s for s in space_blocks}
for name, config in blocks:
block = by_name.get(name)
if block:
Expand Down
43 changes: 23 additions & 20 deletions metablock/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
from httpx import Response as ClientResponse

from .components import Callback, HttpComponent, MetablockResponseError
from .extensions import Extension, Extensions, Plugin, Plugins
from .orgs import Org, Orgs
from .spaces import Block, Blocks, Domains, Space, Spaces
from .extensions import Extensions
from .orgs import Orgs
from .spaces import Blocks, Domains, Spaces
from .user import User

DEFAULT_USER_AGENT = f"Python/{'.'.join(map(str, sys.version_info[:2]))} metablock"
Expand Down Expand Up @@ -42,12 +42,11 @@ def __init__(
"user-agent": user_agent,
"accept": "application/json",
}
self.orgs: Orgs = Orgs(self, Org)
self.spaces: Spaces = Spaces(self, Space)
self.blocks: Blocks = Blocks(self, Block)
self.plugins: Plugins = Plugins(self, Plugin)
self.extensions: Extensions = Extensions(self, Extension)
self.domains = Domains(self)
self.orgs: Orgs = Orgs(root=self, root_path="orgs")
self.spaces: Spaces = Spaces(root=self, root_path="spaces")
self.blocks: Blocks = Blocks(root=self, root_path="blocks")
self.extensions: Extensions = Extensions(root=self, root_path="extensions")
self.domains = Domains(root=self, root_path="domains")

@property
def cli(self) -> Self:
Expand All @@ -65,25 +64,31 @@ async def __aexit__(self, exc_type: type, exc_val: Any, exc_tb: Any) -> None:
await self.close()

async def spec(self) -> dict:
"""Get the OpenAPI specification of the API"""
return await self.request(f"{self.url}/openapi.json")

async def get(self, url: str, **kwargs: Any) -> Any:
"""Make a GET request to the API"""
kwargs["method"] = "GET"
return await self.request(url, **kwargs)

async def patch(self, url: str, **kwargs: Any) -> Any:
"""Make a PATCH request to the API"""
kwargs["method"] = "PATCH"
return await self.request(url, **kwargs)

async def post(self, url: str, **kwargs: Any) -> Any:
"""Make a POST request to the API"""
kwargs["method"] = "POST"
return await self.request(url, **kwargs)

async def put(self, url: str, **kwargs: Any) -> Any:
"""Make a PUT request to the API"""
kwargs["method"] = "PUT"
return await self.request(url, **kwargs)

async def delete(self, url: str, **kwargs: Any) -> Any:
"""Make a DELETE request to the API"""
kwargs["method"] = "DELETE"
return await self.request(url, **kwargs)

Expand All @@ -96,6 +101,7 @@ async def request(
wrap: Any = None,
**kw: Any,
) -> Any:
"""Make a request to the API with the given method, url, headers and body."""
if not self.session:
self.session = AsyncClient()
method = method or "GET"
Expand All @@ -122,22 +128,19 @@ async def handle_response(self, response: ClientResponse, wrap: Any = None) -> A
data = response.json()
return wrap(data) if wrap else data

async def get_user(self, **kw: Any) -> User:
kw.setdefault("wrap", self._user)
return await self.get(f"{self.url}/user", **kw)
async def get_user(self, **kwargs: Any) -> User:
data = await self.get(f"{self.url}/user", **kwargs)
return User(root=self, root_path="user", **data)

async def update_user(self, **kw: Any) -> User:
kw.setdefault("wrap", self._user)
return await self.patch(f"{self.url}/user", **kw)
async def update_user(self, **params: Any) -> User:
data = await self.patch(f"{self.url}/user", **params)
return User(root=self, root_path="user", **data)

async def delete_user(self, **kw: Any) -> None:
return await self.delete(f"{self.url}/user", **kw)
async def delete_user(self) -> None:
return await self.delete(f"{self.url}/user")

def get_default_headers(self) -> dict[str, str]:
headers = self.default_headers.copy()
if self.auth_key:
headers[self.auth_key_name] = self.auth_key
return headers

def _user(self, data: dict) -> User:
return User(self, data)
Loading