Skip to content

Commit 84aa3b7

Browse files
authored
Merge branch 'main' into 177-upgrade-pydantic-version
2 parents 3e88f28 + 5fd68b4 commit 84aa3b7

File tree

10 files changed

+92
-18
lines changed

10 files changed

+92
-18
lines changed

.github/workflows/build-wheels.yml

+2-3
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ jobs:
2727
if: startsWith(matrix.os, 'macos')
2828
uses: actions/setup-python@v2
2929
with:
30-
python-version: 3.11
30+
python-version: 3.12
3131

3232
- name: Cache dependencies
3333
uses: actions/cache@v4
@@ -64,9 +64,8 @@ jobs:
6464
run: |
6565
/tmp/venv/bin/python3 -m pip install dist/aleph_sdk_python-*.whl
6666
67-
- name: Install `setuptools` on systems where it is missing by default
67+
- name: Install/upgrade `setuptools`
6868
run: /tmp/venv/bin/python3 -m pip install --upgrade setuptools
69-
if: matrix.os == 'ubuntu-24.04'
7069

7170
- name: Import and use the package
7271
run: |

.github/workflows/pytest.yml

+6-8
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,19 @@ jobs:
1616
strategy:
1717
fail-fast: false
1818
matrix:
19-
python-version: [ "3.9", "3.10", "3.11" ]
20-
# An issue with secp256k1 prevents Python 3.12 from working
21-
# See https://github.com/baking-bad/pytezos/issues/370
22-
runs-on: ubuntu-latest
19+
python-version: [ "3.9", "3.10", "3.11", "3.12" ]
20+
os: [ubuntu-22.04, ubuntu-24.04]
21+
runs-on: ${{ matrix.os }}
2322

2423
steps:
2524
- uses: actions/checkout@v4
2625
- uses: actions/setup-python@v5
2726
with:
2827
python-version: ${{ matrix.python-version }}
2928

30-
- run: sudo apt-get install -y python3-pip libsecp256k1-dev
29+
- run: |
30+
sudo apt-get update
31+
sudo apt-get install -y python3-pip libsodium-dev
3132
3233
- run: |
3334
python3 -m venv /tmp/venv
@@ -37,13 +38,10 @@ jobs:
3738
/tmp/venv/bin/pip freeze
3839
/tmp/venv/bin/hatch run testing:pip freeze
3940
/tmp/venv/bin/hatch run testing:test
40-
if: matrix.python-version != '3.11'
4141
4242
- run: /tmp/venv/bin/hatch run testing:cov
43-
if: matrix.python-version == '3.11'
4443

4544
- uses: codecov/[email protected]
46-
if: matrix.python-version == '3.11'
4745
with:
4846
token: ${{ secrets.CODECOV_TOKEN }}
4947
slug: aleph-im/aleph-sdk-python

pyproject.toml

+2-2
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ optional-dependencies.encryption = [
6262
"eciespy>=0.3.13; python_version>='3.11'",
6363
]
6464
optional-dependencies.ledger = [
65-
"ledgereth==0.9.1",
65+
"ledgereth==0.10",
6666
]
6767
optional-dependencies.mqtt = [
6868
"aiomqtt<=0.1.3",
@@ -81,7 +81,7 @@ optional-dependencies.substrate = [
8181
"substrate-interface",
8282
]
8383
optional-dependencies.tezos = [
84-
"aleph-pytezos==0.1.1",
84+
"aleph-pytezos==3.13.4",
8585
"pynacl",
8686
]
8787
urls.Documentation = "https://aleph.im/"

src/aleph/sdk/client/abstract.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -430,7 +430,7 @@ async def create_instance(
430430
:param channel: Channel to use (Default: "TEST")
431431
:param address: Address to use (Default: account.get_address())
432432
:param sync: If true, waits for the message to be processed by the API server
433-
:param memory: Memory in MB for the VM to be allocated (Default: 128)
433+
:param memory: Memory in MB for the VM to be allocated (Default: 2048)
434434
:param vcpus: Number of vCPUs to allocate (Default: 1)
435435
:param timeout_seconds: Timeout in seconds (Default: 30.0)
436436
:param allow_amend: Whether the deployed VM image may be changed (Default: False)

src/aleph/sdk/client/http.py

+36-2
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
import aiohttp
2121
from aiohttp.web import HTTPNotFound
2222
from aleph_message import parse_message
23-
from aleph_message.models import AlephMessage, ItemHash, ItemType
23+
from aleph_message.models import AlephMessage, ItemHash, ItemType, MessageType
2424
from aleph_message.status import MessageStatus
2525
from pydantic import ValidationError
2626

@@ -33,13 +33,14 @@
3333
)
3434
from ..query.filters import MessageFilter, PostFilter
3535
from ..query.responses import MessagesResponse, Post, PostsResponse, PriceResponse
36-
from ..types import GenericMessage
36+
from ..types import GenericMessage, StoredContent
3737
from ..utils import (
3838
Writable,
3939
check_unix_socket_valid,
4040
copy_async_readable_to_buffer,
4141
extended_json_encoder,
4242
get_message_type_value,
43+
safe_getattr,
4344
)
4445
from .abstract import AlephClient
4546

@@ -469,3 +470,36 @@ async def get_message_status(self, item_hash: str) -> MessageStatus:
469470
resp.raise_for_status()
470471
result = await resp.json()
471472
return MessageStatus(result["status"])
473+
474+
async def get_stored_content(
475+
self,
476+
item_hash: str,
477+
) -> StoredContent:
478+
"""return the underlying content for a store message"""
479+
480+
result, resp = None, None
481+
try:
482+
message: AlephMessage
483+
message, status = await self.get_message(
484+
item_hash=ItemHash(item_hash), with_status=True
485+
)
486+
if status != MessageStatus.PROCESSED:
487+
resp = f"Invalid message status: {status}"
488+
elif message.type != MessageType.store:
489+
resp = f"Invalid message type: {message.type}"
490+
elif not message.content.item_hash:
491+
resp = f"Invalid CID: {message.content.item_hash}"
492+
else:
493+
filename = safe_getattr(message.content, "metadata.name")
494+
hash = message.content.item_hash
495+
url = (
496+
f"{self.api_server}/api/v0/storage/raw/"
497+
if len(hash) == 64
498+
else settings.IPFS_GATEWAY
499+
) + hash
500+
result = StoredContent(filename=filename, hash=hash, url=url)
501+
except MessageNotFoundError:
502+
resp = f"Message not found: {item_hash}"
503+
except ForgottenMessageError:
504+
resp = f"Message forgotten: {item_hash}"
505+
return result if result else StoredContent(error=resp)

src/aleph/sdk/client/vm_client.py

+27
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from urllib.parse import urlparse
66

77
import aiohttp
8+
from aiohttp.client import _RequestContextManager
89
from aleph_message.models import Chain, ItemHash
910
from eth_account.messages import encode_defunct
1011
from jwcrypto import jwk
@@ -127,6 +128,32 @@ async def perform_operation(
127128
logger.error(f"HTTP error during operation {operation}: {str(e)}")
128129
return None, str(e)
129130

131+
def operate(
132+
self, vm_id: ItemHash, operation: str, method: str = "POST"
133+
) -> _RequestContextManager:
134+
"""Request a CRN an operation for a VM (eg reboot, logs)
135+
136+
This operation is authenticated via the user wallet.
137+
Use as an async context manager.
138+
e.g `async with client.operate(vm_id=item_hash, operation="logs", method="GET") as response:`
139+
"""
140+
141+
async def authenticated_request():
142+
if not self.pubkey_signature_header:
143+
self.pubkey_signature_header = (
144+
await self._generate_pubkey_signature_header()
145+
)
146+
147+
url, header = await self._generate_header(
148+
vm_id=vm_id, operation=operation, method=method
149+
)
150+
resp = await self.session._request(
151+
method=method, str_or_url=url, headers=header
152+
)
153+
return resp
154+
155+
return _RequestContextManager(authenticated_request())
156+
130157
async def get_logs(self, vm_id: ItemHash) -> AsyncGenerator[str, None]:
131158
if not self.pubkey_signature_header:
132159
self.pubkey_signature_header = (

src/aleph/sdk/conf.py

+1
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ class Settings(BaseSettings):
8686

8787
VM_URL_PATH: ClassVar[str] = "https://aleph.sh/vm/{hash}"
8888
VM_URL_HOST: ClassVar[str] = "https://{hash_base32}.aleph.sh"
89+
IPFS_GATEWAY = "https://ipfs.aleph.cloud/ipfs/"
8990

9091
# Web3Provider settings
9192
TOKEN_DECIMALS: ClassVar[int] = 18

src/aleph/sdk/types.py

+7
Original file line numberDiff line numberDiff line change
@@ -80,3 +80,10 @@ class ChainInfo(BaseModel):
8080
token: Optional[str] = None
8181
super_token: Optional[str] = None
8282
active: bool = True
83+
84+
85+
class StoredContent(BaseModel):
86+
filename: Optional[str]
87+
hash: Optional[str]
88+
url: Optional[str]
89+
error: Optional[str]

src/aleph/sdk/utils.py

+8
Original file line numberDiff line numberDiff line change
@@ -389,3 +389,11 @@ def make_packet_header(
389389
header[20:52] = h.digest()
390390

391391
return header
392+
393+
394+
def safe_getattr(obj, attr, default=None):
395+
for part in attr.split("."):
396+
obj = getattr(obj, part, default)
397+
if obj is default:
398+
break
399+
return obj

tests/unit/test_chain_tezos.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ async def test_tezos_account(tezos_account: TezosAccount):
3131
message = Message("TEZOS", tezos_account.get_address(), "SomeType", "ItemHash")
3232
signed = await tezos_account.sign_message(asdict(message))
3333
assert signed["signature"]
34-
assert len(signed["signature"]) == 188
34+
assert len(signed["signature"]) == 187
3535

3636
address = tezos_account.get_address()
3737
assert address is not None
@@ -40,7 +40,7 @@ async def test_tezos_account(tezos_account: TezosAccount):
4040

4141
pubkey = tezos_account.get_public_key()
4242
assert isinstance(pubkey, str)
43-
assert len(pubkey) == 55
43+
assert len(pubkey) == 54
4444

4545

4646
@pytest.mark.asyncio

0 commit comments

Comments
 (0)