Skip to content

Commit 0c848b5

Browse files
AntonyjinPsycojokerphilogicaeolethanhnesitor
authored
Upgrade pydantic version (#179)
* Migrate to Pydantic v2, update model validation and fix async issues - Migrated to Pydantic v2: - Replaced deprecated `parse_obj()` and `parse_raw()` with `model_validate()` and `model_validate_json()`. - Replaced `.dict()` with `.model_dump()` for serializing models to dictionaries. - Updated `validator` to `field_validator` and `root_validator` to `model_validator` to comply with Pydantic v2 syntax changes. - Fixed asyncio issues: - Added `await` for asynchronous methods like `raise_for_status()` in `RemoteAccount` and other HTTP operations to avoid `RuntimeWarning`. - Updated config handling: - Used `ClassVar` for constants in `Settings` and other configuration classes. - Replaced `Config` with `ConfigDict` in Pydantic models to follow v2 conventions. - Added default values for missing fields in chain configurations (`CHAINS_SEPOLIA_ACTIVE`, etc.). - Adjusted signature handling: - Updated the signing logic to prepend `0x` in the `BaseAccount` signature generation to ensure correct Ethereum address formatting. - Minor fixes: - Resolved issue with extra fields not being allowed by default by specifying `extra="allow"` or `extra="forbid"` where necessary. - Fixed tests to account for changes in model validation and serialization behavior. - Added `pydantic-settings` as a new dependency for configuration management. * fix: lint tests were failing - Updated all instances of **extra_fields to ensure proper handling of Optional dictionaries using `(extra_fields or {})` pattern. - Added proper return statements in `AlephHttpClient.get_message_status` to return parsed JSON data as a `MessageStatus` object. - Updated `Settings` class in `conf.py` to correct DNS resolvers type and simplify the `model_config` definition. - Refactored `parse_volume` to ensure correct handling of Mapping types and MachineVolume types, avoiding TypeErrors. - Improved field validation and model validation in `SignedPubKeyHeader` by using correct Pydantic v2 validation decorators and ensuring compatibility with the new model behavior. - Applied formatting and consistency fixes for `model_dump` usage and indentation improvements in test files. * feat: add pyproject-fmt * fix: run pyproject-fmt * Post-SOL fixes (#178) * Missing chain field on auth * Fix Signature of Solana operation for CRN * Add export_private_key func for accounts * Improve _load_account * Add chain arg to _load_account * Increase default HTTP_REQUEST_TIMEOUT * Typing --------- Co-authored-by: Olivier Le Thanh Duong <[email protected]> * Migrate to Pydantic v2, update model validation and fix async issues - Migrated to Pydantic v2: - Replaced deprecated `parse_obj()` and `parse_raw()` with `model_validate()` and `model_validate_json()`. - Replaced `.dict()` with `.model_dump()` for serializing models to dictionaries. - Updated `validator` to `field_validator` and `root_validator` to `model_validator` to comply with Pydantic v2 syntax changes. - Fixed asyncio issues: - Added `await` for asynchronous methods like `raise_for_status()` in `RemoteAccount` and other HTTP operations to avoid `RuntimeWarning`. - Updated config handling: - Used `ClassVar` for constants in `Settings` and other configuration classes. - Replaced `Config` with `ConfigDict` in Pydantic models to follow v2 conventions. - Added default values for missing fields in chain configurations (`CHAINS_SEPOLIA_ACTIVE`, etc.). - Adjusted signature handling: - Updated the signing logic to prepend `0x` in the `BaseAccount` signature generation to ensure correct Ethereum address formatting. - Minor fixes: - Resolved issue with extra fields not being allowed by default by specifying `extra="allow"` or `extra="forbid"` where necessary. - Fixed tests to account for changes in model validation and serialization behavior. - Added `pydantic-settings` as a new dependency for configuration management. * fix: add explicit float type for HTTP_REQUEST_TIMEOUT to comply with Pydantic v2 requirements Pydantic v2 requires explicit type annotations for fields, so added `float` to ensure proper validation of HTTP_REQUEST_TIMEOUT. * Fix: Linting tests did not pass: * Fix: Project don't use the good version of aleph-message There were changes made on aleph-message on the main branch about pydantic version. Using the version by the url and then change it later after the release. * fix: Wrong aleph-message version * Fix: list[str] rise an error in ubuntu 20.04 Using List from typing instead to assure the compatibility between python3.8 and above * style: isort * fix: Hugo comments * Add pydantic for better mypy tests + Fixes * fix: Changing version of aleph-message * style: Missing type for URL * style: Missing type for URL * fix: Changing version of aleph-message and fix mypy Changing the version from the branch to the main of aleph-message mypy rose some errors about missing name argument, so setting the as None because they are optional * fix: Changing version of aleph-message * fix: Changing version of pytezos * Changes for new pricing system (#199) - Move/improve flow code parts from CLI to SDK - Add utils functions - Add `make_instance_content` and `make_program_content` - Refactor `create_instance` and `create_program` - Add `get_estimated_price` - Fixes for mypy/ruff/pytest - Minor improvements - Remove firecracker rootfs hashes for instances * Migrate to Pydantic v2, update model validation and fix async issues - Migrated to Pydantic v2: - Replaced deprecated `parse_obj()` and `parse_raw()` with `model_validate()` and `model_validate_json()`. - Replaced `.dict()` with `.model_dump()` for serializing models to dictionaries. - Updated `validator` to `field_validator` and `root_validator` to `model_validator` to comply with Pydantic v2 syntax changes. - Fixed asyncio issues: - Added `await` for asynchronous methods like `raise_for_status()` in `RemoteAccount` and other HTTP operations to avoid `RuntimeWarning`. - Updated config handling: - Used `ClassVar` for constants in `Settings` and other configuration classes. - Replaced `Config` with `ConfigDict` in Pydantic models to follow v2 conventions. - Added default values for missing fields in chain configurations (`CHAINS_SEPOLIA_ACTIVE`, etc.). - Adjusted signature handling: - Updated the signing logic to prepend `0x` in the `BaseAccount` signature generation to ensure correct Ethereum address formatting. - Minor fixes: - Resolved issue with extra fields not being allowed by default by specifying `extra="allow"` or `extra="forbid"` where necessary. - Fixed tests to account for changes in model validation and serialization behavior. - Added `pydantic-settings` as a new dependency for configuration management. * fix: lint tests were failing - Updated all instances of **extra_fields to ensure proper handling of Optional dictionaries using `(extra_fields or {})` pattern. - Added proper return statements in `AlephHttpClient.get_message_status` to return parsed JSON data as a `MessageStatus` object. - Updated `Settings` class in `conf.py` to correct DNS resolvers type and simplify the `model_config` definition. - Refactored `parse_volume` to ensure correct handling of Mapping types and MachineVolume types, avoiding TypeErrors. - Improved field validation and model validation in `SignedPubKeyHeader` by using correct Pydantic v2 validation decorators and ensuring compatibility with the new model behavior. - Applied formatting and consistency fixes for `model_dump` usage and indentation improvements in test files. * Migrate to Pydantic v2, update model validation and fix async issues - Migrated to Pydantic v2: - Replaced deprecated `parse_obj()` and `parse_raw()` with `model_validate()` and `model_validate_json()`. - Replaced `.dict()` with `.model_dump()` for serializing models to dictionaries. - Updated `validator` to `field_validator` and `root_validator` to `model_validator` to comply with Pydantic v2 syntax changes. - Fixed asyncio issues: - Added `await` for asynchronous methods like `raise_for_status()` in `RemoteAccount` and other HTTP operations to avoid `RuntimeWarning`. - Updated config handling: - Used `ClassVar` for constants in `Settings` and other configuration classes. - Replaced `Config` with `ConfigDict` in Pydantic models to follow v2 conventions. - Added default values for missing fields in chain configurations (`CHAINS_SEPOLIA_ACTIVE`, etc.). - Adjusted signature handling: - Updated the signing logic to prepend `0x` in the `BaseAccount` signature generation to ensure correct Ethereum address formatting. - Minor fixes: - Resolved issue with extra fields not being allowed by default by specifying `extra="allow"` or `extra="forbid"` where necessary. - Fixed tests to account for changes in model validation and serialization behavior. - Added `pydantic-settings` as a new dependency for configuration management. * Fix: Linting tests did not pass: * fix: Wrong aleph-message version * fix: Hugo comments * Add pydantic for better mypy tests + Fixes * fix: Changing version of aleph-message * style: Missing type for URL * fix: Changing version of aleph-message and fix mypy Changing the version from the branch to the main of aleph-message mypy rose some errors about missing name argument, so setting the as None because they are optional * fix: Changing version of aleph-message * Fix: Missing pydantic_core and wrong version of tezos * Fix: Access to PersistentVolumeSizeMib is incompatible after migrating to Pydantic2 Using model_validate to access it * Fix: Wrong name given to the variable * Style: isort * Fix: PersistentVolumeSizeMib no longer exist This class has been deleted from aleph_message and the size is now inside the PersistentVolume class * Fix: Update last `aleph-message` version and use again `PersistentVolumeSizeMib` class * fix: invalid signature cause by `0x` + signature.hex() * fix: add '0x' to the signature if not here (error happenings only on unit test) * Refactor: Apply the `.hex()` quick fix on the ETHAccount class instead on the base one as other chains can be affected. * fix: pydantic model should use `.model_dump()` instead of `dict()` * fix: add dummy signature for unauthenticated price estimates When estimating prices without authentication, there's no valid signature available. This fix uses a dummy signature so that message validation passes in these cases. --------- Co-authored-by: Laurent Peuch <[email protected]> Co-authored-by: philogicae <[email protected]> Co-authored-by: Olivier Le Thanh Duong <[email protected]> Co-authored-by: philogicae <[email protected]> Co-authored-by: Andres D. Molins <[email protected]> Co-authored-by: 1yam <[email protected]> Co-authored-by: Andres D. Molins <[email protected]>
1 parent e40033f commit 0c848b5

19 files changed

+134
-114
lines changed

pyproject.toml

+5-3
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,17 @@ dynamic = [ "version" ]
3030
dependencies = [
3131
"aiohttp>=3.8.3",
3232
"aioresponses>=0.7.6",
33-
"aleph-message @ git+https://github.com/aleph-im/aleph-message.git@andres-feature-integrate_sonic_blockchain",
33+
"aleph-message>=1",
3434
"aleph-superfluid>=0.2.1",
35-
"base58==2.1.1", # Needed now as default with _load_account changement
35+
"base58==2.1.1", # Needed now as default with _load_account changement
3636
"coincurve; python_version<'3.11'",
3737
"coincurve>=19; python_version>='3.11'",
3838
"eth-abi>=4; python_version>='3.11'",
3939
"eth-typing==4.3.1",
4040
"jwcrypto==1.5.6",
41-
"pynacl==1.5", # Needed now as default with _load_account changement
41+
"pydantic>=2,<3",
42+
"pydantic-settings>=2",
43+
"pynacl==1.5", # Needed now as default with _load_account changement
4244
"python-magic",
4345
"typing-extensions",
4446
"web3==6.3",

src/aleph/sdk/chains/common.py

+1
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ async def sign_message(self, message: Dict) -> Dict:
7373
message = self._setup_sender(message)
7474
signature = await self.sign_raw(get_verification_buffer(message))
7575
message["signature"] = signature.hex()
76+
7677
return message
7778

7879
@abstractmethod

src/aleph/sdk/chains/ethereum.py

+17-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import base64
33
from decimal import Decimal
44
from pathlib import Path
5-
from typing import Awaitable, Optional, Union
5+
from typing import Awaitable, Dict, Optional, Union
66

77
from aleph_message.models import Chain
88
from eth_account import Account # type: ignore
@@ -80,6 +80,22 @@ async def sign_raw(self, buffer: bytes) -> bytes:
8080
sig = self._account.sign_message(msghash)
8181
return sig["signature"]
8282

83+
async def sign_message(self, message: Dict) -> Dict:
84+
"""
85+
Returns a signed message from an aleph.im message.
86+
Args:
87+
message: Message to sign
88+
Returns:
89+
Dict: Signed message
90+
"""
91+
signed_message = await super().sign_message(message)
92+
93+
# Apply that fix as seems that sometimes the .hex() method doesn't add the 0x str at the beginning
94+
if not str(signed_message["signature"]).startswith("0x"):
95+
signed_message["signature"] = "0x" + signed_message["signature"]
96+
97+
return signed_message
98+
8399
def connect_chain(self, chain: Optional[Chain] = None):
84100
self.chain = chain
85101
if self.chain:

src/aleph/sdk/chains/remote.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ async def from_crypto_host(
5252
session = aiohttp.ClientSession(connector=connector)
5353

5454
async with session.get(f"{host}/properties") as response:
55-
response.raise_for_status()
55+
await response.raise_for_status()
5656
data = await response.json()
5757
properties = AccountProperties(**data)
5858

@@ -75,7 +75,7 @@ def private_key(self):
7575
async def sign_message(self, message: Dict) -> Dict:
7676
"""Sign a message inplace."""
7777
async with self._session.post(f"{self._host}/sign", json=message) as response:
78-
response.raise_for_status()
78+
await response.raise_for_status()
7979
return await response.json()
8080

8181
async def sign_raw(self, buffer: bytes) -> bytes:

src/aleph/sdk/client/authenticated_http.py

+10-10
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ async def _broadcast(
251251
url = "/api/v0/messages"
252252
logger.debug(f"Posting message on {url}")
253253

254-
message_dict = message.dict(include=self.BROADCAST_MESSAGE_FIELDS)
254+
message_dict = message.model_dump(include=self.BROADCAST_MESSAGE_FIELDS)
255255
async with self.http_session.post(
256256
url,
257257
json={
@@ -293,7 +293,7 @@ async def create_post(
293293
)
294294

295295
message, status, _ = await self.submit(
296-
content=content.dict(exclude_none=True),
296+
content=content.model_dump(exclude_none=True),
297297
message_type=MessageType.post,
298298
channel=channel,
299299
allow_inlining=inline,
@@ -321,7 +321,7 @@ async def create_aggregate(
321321
)
322322

323323
message, status, _ = await self.submit(
324-
content=content_.dict(exclude_none=True),
324+
content=content_.model_dump(exclude_none=True),
325325
message_type=MessageType.aggregate,
326326
channel=channel,
327327
allow_inlining=inline,
@@ -395,7 +395,7 @@ async def create_store(
395395
content = StoreContent.parse_obj(values)
396396

397397
message, status, _ = await self.submit(
398-
content=content.dict(exclude_none=True),
398+
content=content.model_dump(exclude_none=True),
399399
message_type=MessageType.store,
400400
channel=channel,
401401
allow_inlining=True,
@@ -449,7 +449,7 @@ async def create_program(
449449
)
450450

451451
message, status, _ = await self.submit(
452-
content=content.dict(exclude_none=True),
452+
content=content.model_dump(exclude_none=True),
453453
message_type=MessageType.program,
454454
channel=channel,
455455
storage_engine=storage_engine,
@@ -525,7 +525,7 @@ async def create_instance(
525525
)
526526

527527
message, status, response = await self.submit(
528-
content=content.dict(exclude_none=True),
528+
content=content.model_dump(exclude_none=True),
529529
message_type=MessageType.instance,
530530
channel=channel,
531531
storage_engine=storage_engine,
@@ -573,7 +573,7 @@ async def forget(
573573
)
574574

575575
message, status, _ = await self.submit(
576-
content=content.dict(exclude_none=True),
576+
content=content.model_dump(exclude_none=True),
577577
message_type=MessageType.forget,
578578
channel=channel,
579579
storage_engine=storage_engine,
@@ -617,11 +617,11 @@ async def _storage_push_file_with_message(
617617
# Prepare the STORE message
618618
message = await self.generate_signed_message(
619619
message_type=MessageType.store,
620-
content=store_content.dict(exclude_none=True),
620+
content=store_content.model_dump(exclude_none=True),
621621
channel=channel,
622622
)
623623
metadata = {
624-
"message": message.dict(exclude_none=True),
624+
"message": message.model_dump(exclude_none=True),
625625
"sync": sync,
626626
}
627627
data.add_field(
@@ -665,7 +665,7 @@ async def _upload_file_native(
665665
item_hash=ItemHash(file_hash),
666666
mime_type=mime_type, # type: ignore
667667
time=time.time(),
668-
**extra_fields,
668+
**(extra_fields or {}),
669669
)
670670
message, _ = await self._storage_push_file_with_message(
671671
file_content=file_content,

src/aleph/sdk/client/http.py

+19-18
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ async def get_posts(
191191
posts: List[Post] = []
192192
for post_raw in posts_raw:
193193
try:
194-
posts.append(Post.parse_obj(post_raw))
194+
posts.append(Post.model_validate(post_raw))
195195
except ValidationError as e:
196196
if not ignore_invalid_messages:
197197
raise e
@@ -462,30 +462,31 @@ async def get_estimated_price(
462462
self,
463463
content: ExecutableContent,
464464
) -> PriceResponse:
465-
cleaned_content = content.dict(exclude_none=True)
465+
cleaned_content = content.model_dump(exclude_none=True)
466466
item_content: str = json.dumps(
467467
cleaned_content,
468468
separators=(",", ":"),
469469
default=extended_json_encoder,
470470
)
471-
message = parse_message(
472-
dict(
473-
sender=content.address,
474-
chain=Chain.ETH,
475-
type=(
476-
MessageType.program
477-
if isinstance(content, ProgramContent)
478-
else MessageType.instance
479-
),
480-
content=cleaned_content,
481-
item_content=item_content,
482-
time=time.time(),
483-
channel=settings.DEFAULT_CHANNEL,
484-
item_type=ItemType.inline,
485-
item_hash=compute_sha256(item_content),
486-
)
471+
message_dict = dict(
472+
sender=content.address,
473+
chain=Chain.ETH,
474+
type=(
475+
MessageType.program
476+
if isinstance(content, ProgramContent)
477+
else MessageType.instance
478+
),
479+
content=cleaned_content,
480+
item_content=item_content,
481+
time=time.time(),
482+
channel=settings.DEFAULT_CHANNEL,
483+
item_type=ItemType.inline,
484+
item_hash=compute_sha256(item_content),
485+
signature="0x" + "0" * 130, # Add a dummy signature to pass validation
487486
)
488487

488+
message = parse_message(message_dict)
489+
489490
async with self.http_session.post(
490491
"/api/v0/price/estimate", json=dict(message=message)
491492
) as resp:

src/aleph/sdk/client/vm_confidential_client.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ async def measurement(self, vm_id: ItemHash) -> SEVMeasurement:
105105
status, text = await self.perform_operation(
106106
vm_id, "confidential/measurement", method="GET"
107107
)
108-
sev_measurement = SEVMeasurement.parse_raw(text)
108+
sev_measurement = SEVMeasurement.model_validate_json(text)
109109
return sev_measurement
110110

111111
async def validate_measure(

src/aleph/sdk/conf.py

+21-22
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@
33
import os
44
from pathlib import Path
55
from shutil import which
6-
from typing import Dict, Optional, Union
6+
from typing import ClassVar, Dict, List, Optional, Union
77

88
from aleph_message.models import Chain
99
from aleph_message.models.execution.environment import HypervisorType
10-
from pydantic import BaseModel, BaseSettings, Field
10+
from pydantic import BaseModel, Field
11+
from pydantic_settings import BaseSettings, SettingsConfigDict
1112

1213
from aleph.sdk.types import ChainInfo
1314

@@ -41,7 +42,7 @@ class Settings(BaseSettings):
4142
REMOTE_CRYPTO_HOST: Optional[str] = None
4243
REMOTE_CRYPTO_UNIX_SOCKET: Optional[str] = None
4344
ADDRESS_TO_USE: Optional[str] = None
44-
HTTP_REQUEST_TIMEOUT = 15.0
45+
HTTP_REQUEST_TIMEOUT: ClassVar[float] = 15.0
4546

4647
DEFAULT_CHANNEL: str = "ALEPH-CLOUDSOLUTIONS"
4748

@@ -78,14 +79,14 @@ class Settings(BaseSettings):
7879

7980
CODE_USES_SQUASHFS: bool = which("mksquashfs") is not None # True if command exists
8081

81-
VM_URL_PATH = "https://aleph.sh/vm/{hash}"
82-
VM_URL_HOST = "https://{hash_base32}.aleph.sh"
83-
IPFS_GATEWAY = "https://ipfs.aleph.cloud/ipfs/"
84-
CRN_URL_FOR_PROGRAMS = "https://dchq.staging.aleph.sh/"
82+
VM_URL_PATH: ClassVar[str] = "https://aleph.sh/vm/{hash}"
83+
VM_URL_HOST: ClassVar[str] = "https://{hash_base32}.aleph.sh"
84+
IPFS_GATEWAY: ClassVar[str] = "https://ipfs.aleph.cloud/ipfs/"
85+
CRN_URL_FOR_PROGRAMS: ClassVar[str] = "https://dchq.staging.aleph.sh/"
8586

8687
# Web3Provider settings
87-
TOKEN_DECIMALS = 18
88-
TX_TIMEOUT = 60 * 3
88+
TOKEN_DECIMALS: ClassVar[int] = 18
89+
TX_TIMEOUT: ClassVar[int] = 60 * 3
8990
CHAINS: Dict[Union[Chain, str], ChainInfo] = {
9091
# TESTNETS
9192
"SEPOLIA": ChainInfo(
@@ -220,16 +221,15 @@ class Settings(BaseSettings):
220221
DEFAULT_CHAIN: Chain = Chain.ETH
221222

222223
# Dns resolver
223-
DNS_IPFS_DOMAIN = "ipfs.public.aleph.sh"
224-
DNS_PROGRAM_DOMAIN = "program.public.aleph.sh"
225-
DNS_INSTANCE_DOMAIN = "instance.public.aleph.sh"
226-
DNS_STATIC_DOMAIN = "static.public.aleph.sh"
227-
DNS_RESOLVERS = ["9.9.9.9", "1.1.1.1"]
228-
229-
class Config:
230-
env_prefix = "ALEPH_"
231-
case_sensitive = False
232-
env_file = ".env"
224+
DNS_IPFS_DOMAIN: ClassVar[str] = "ipfs.public.aleph.sh"
225+
DNS_PROGRAM_DOMAIN: ClassVar[str] = "program.public.aleph.sh"
226+
DNS_INSTANCE_DOMAIN: ClassVar[str] = "instance.public.aleph.sh"
227+
DNS_STATIC_DOMAIN: ClassVar[str] = "static.public.aleph.sh"
228+
DNS_RESOLVERS: ClassVar[List[str]] = ["9.9.9.9", "1.1.1.1"]
229+
230+
model_config = SettingsConfigDict(
231+
env_prefix="ALEPH_", case_sensitive=False, env_file=".env"
232+
)
233233

234234

235235
class MainConfiguration(BaseModel):
@@ -240,8 +240,7 @@ class MainConfiguration(BaseModel):
240240
path: Path
241241
chain: Chain
242242

243-
class Config:
244-
use_enum_values = True
243+
model_config = SettingsConfigDict(use_enum_values=True)
245244

246245

247246
# Settings singleton
@@ -297,7 +296,7 @@ def save_main_configuration(file_path: Path, data: MainConfiguration):
297296
Synchronously save a single ChainAccount object as JSON to a file.
298297
"""
299298
with file_path.open("w") as file:
300-
data_serializable = data.dict()
299+
data_serializable = data.model_dump()
301300
data_serializable["path"] = str(data_serializable["path"])
302301
json.dump(data_serializable, file, indent=4)
303302

src/aleph/sdk/domain.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,11 @@ def raise_error(self, status: Dict[str, bool]):
5252
def hostname_from_url(url: Union[HttpUrl, str]) -> Hostname:
5353
"""Extract FQDN from url"""
5454

55-
parsed = urlparse(url)
55+
parsed = urlparse(str(url))
5656
if all([parsed.scheme, parsed.netloc]) is True:
5757
url = parsed.netloc
5858

59-
return Hostname(url)
59+
return Hostname(str(url))
6060

6161

6262
async def get_target_type(fqdn: Hostname) -> Optional[TargetType]:

src/aleph/sdk/query/responses.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
ItemType,
1010
MessageConfirmation,
1111
)
12-
from pydantic import BaseModel, Field
12+
from pydantic import BaseModel, ConfigDict, Field
1313

1414

1515
class Post(BaseModel):
@@ -48,9 +48,9 @@ class Post(BaseModel):
4848
ref: Optional[Union[str, Any]] = Field(
4949
description="Other message referenced by this one"
5050
)
51+
address: Optional[str] = Field(description="Address of the sender")
5152

52-
class Config:
53-
allow_extra = False
53+
model_config = ConfigDict(extra="forbid")
5454

5555

5656
class PaginationResponse(BaseModel):
@@ -64,14 +64,14 @@ class PostsResponse(PaginationResponse):
6464
"""Response from an aleph.im node API on the path /api/v0/posts.json"""
6565

6666
posts: List[Post]
67-
pagination_item = "posts"
67+
pagination_item: str = "posts"
6868

6969

7070
class MessagesResponse(PaginationResponse):
7171
"""Response from an aleph.im node API on the path /api/v0/messages.json"""
7272

7373
messages: List[AlephMessage]
74-
pagination_item = "messages"
74+
pagination_item: str = "messages"
7575

7676

7777
class PriceResponse(BaseModel):

src/aleph/sdk/types.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from enum import Enum
33
from typing import Dict, Optional, Protocol, TypeVar
44

5-
from pydantic import BaseModel
5+
from pydantic import BaseModel, Field
66

77
__all__ = ("StorageEnum", "Account", "AccountFromPrivateKey", "GenericMessage")
88

@@ -87,10 +87,10 @@ class StoredContent(BaseModel):
8787
A stored content.
8888
"""
8989

90-
filename: Optional[str]
91-
hash: Optional[str]
92-
url: Optional[str]
93-
error: Optional[str]
90+
filename: Optional[str] = Field(default=None)
91+
hash: Optional[str] = Field(default=None)
92+
url: Optional[str] = Field(default=None)
93+
error: Optional[str] = Field(default=None)
9494

9595

9696
class TokenType(str, Enum):

0 commit comments

Comments
 (0)