Skip to content

Commit 6a9d348

Browse files
Antonyjinnesitor
andauthored
Migrate from Pydantic V1 to V2 (#293)
* Fix: Project do not use the good version of aleph message and sdk Aleph-message and sdk now uses pydantic v2 but no released yet Using the github link atm and wait for the release. * Fix: Replacing deprecated function after pydantic migration When upgrading the pydantic version, some function are / will become deprecated. replacing them in those files. * feat: Adding custom encoding for JSON serialization Added `default=extended_json_encoder` to `json.dumps()` to handle serialization of non-native JSON types, such as dates and other complex objects. Replacing function that are / will be deprecated in pydantic v2 and v3 * Fix: Conflit between pydantic and fastapi version Pydantic>=2 is not compatible with fastapi<0.100.0 * Fix: Lint test did not pass and conflict between dependencies Conflit between pydantic and yamlfix black did not pass * fix: Wrong aleph-message, aleph-python-sdk version * fix: Pydantic 2 being more strict about type validation With Pydantic v2, strict type validation is enforced. The type field is expected to have the exact value "POST" as a literal. * change version of typing_extension * change version of typing_extension * style: ymlfix * style: pyproject-fmt * style: ruff - Unused varible deleted * Fix: Functions are deprecated because of the migration to pydantic2 - Replacing parse_obj() by model_validate() - Replacing copy() by model_copy() - Replacing dict() by model_dump() * fix: Warning about dict() being deprecated * Fix: We can't access size_mib about Ephemeral and Persistant class because of some changes on aleph_message Before any change, there were classes dedicated to the size of Ephemeral and Persistent class such as PersistentVolumeSizeMb and EphemeralVolumeSizeMb. Now, the size is included inside the classes EphemeralVolume and PersistentVolume so have to access the size by another method. - Creating a function to get the max and min size, that way it is more rigorous - Calling this function to get max and min size of the different volumes * style: mypy raise errors about unexpected arguments - Refactor the Dict() class inside the mocks test file to allow any key-value pair to be passed to the model. * fix: Missing substrate-interface depedency * Fix: Reverting some changes I made Arnaud made some changes about getting hashes that were better but I deleted by mistakes Putting back his code * Fix: aleph-message version conflicts between aleph-client and aleph-sdk * Fix: Make changes from Andres review Reverting changes about accessing data of PersistentVolumeSizeMib and EphemeralVolumeSize - creating a function to access annotated type because we can no longer do Persistent.le to get the data * Fix: Solved last issues on some commands and refactored the method to get the annotation rules. * Fix: Update the SDK dependency to the last 2.0.0 version. --------- Co-authored-by: Andres D. Molins <[email protected]>
1 parent f5c89eb commit 6a9d348

File tree

13 files changed

+88
-53
lines changed

13 files changed

+88
-53
lines changed

pyproject.toml

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,17 @@ dynamic = [ "version" ]
3030
dependencies = [
3131
"aiodns==3.2",
3232
"aiohttp==3.11.13",
33-
"aleph-message>=0.6.1",
34-
"aleph-sdk-python @ git+https://github.com/aleph-im/aleph-sdk-python",
35-
"base58==2.1.1", # Needed now as default with _load_account changement
36-
"py-sr25519-bindings==0.2", # Needed for DOT signatures
33+
"aleph-message>=1",
34+
"aleph-sdk-python>=2,<3",
35+
"base58==2.1.1", # Needed now as default with _load_account changement
36+
"py-sr25519-bindings==0.2", # Needed for DOT signatures
37+
"pydantic>=2",
3738
"pygments==2.19.1",
38-
"pynacl==1.5", # Needed now as default with _load_account changement
39+
"pynacl==1.5", # Needed now as default with _load_account changement
3940
"python-magic==0.4.27",
4041
"rich==13.9.*",
4142
"setuptools>=65.5",
42-
"substrate-interface==1.7.11", # Needed for DOT signatures
43+
"substrate-interface==1.7.11", # Needed for DOT signatures
4344
"textual==0.73",
4445
"typer==0.15.2",
4546
]
@@ -80,7 +81,7 @@ dependencies = [
8081
"pytest-cov==5.0.0",
8182
"mypy==1.10.0",
8283
"base58==2.1.1",
83-
"fastapi==0.98.0",
84+
"fastapi==0.100.0",
8485
"httpx==0.27.0",
8586
"types-requests==2.32.0.20240602",
8687
"types-setuptools==70.0.0.20240524",
@@ -96,7 +97,7 @@ dependencies = [
9697
"pytest-cov==5.0.0",
9798
"mypy==1.10.0",
9899
"base58==2.1.1",
99-
"fastapi==0.98.0",
100+
"fastapi==0.100.0",
100101
"httpx==0.27.0",
101102
]
102103
[tool.hatch.envs.testing.scripts]
@@ -119,7 +120,7 @@ dependencies = [
119120
"mypy==1.10.0",
120121
"ruff==0.9.*",
121122
"isort==5.13.2",
122-
"yamlfix==1.16.1",
123+
"yamlfix==1.17.0",
123124
"pyproject-fmt==2.2.1",
124125

125126
"types-requests==2.32.0.20240602",

src/aleph_client/commands/account.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@ def sign_bytes(
267267
assert message is not None # to please mypy
268268
coroutine = account.sign_raw(str(message).encode())
269269
signature = asyncio.run(coroutine)
270-
typer.echo("\nSignature: " + signature.hex())
270+
typer.echo("\nSignature: " + f"0x{signature.hex()}")
271271

272272

273273
async def get_balance(address: str) -> dict:

src/aleph_client/commands/aggregate.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ async def forget(
8888
inline=inline,
8989
address=address,
9090
)
91-
dumped_content = f"{message.json(indent=4)}"
91+
dumped_content = f"{message.model_dump_json(indent=4)}"
9292

9393
if status != MessageStatus.REJECTED:
9494
if print_message:
@@ -159,7 +159,7 @@ async def post(
159159
inline=inline,
160160
address=address,
161161
)
162-
content = f"{message.json(indent=4)}"
162+
content = f"{message.model_dump_json(indent=4)}"
163163

164164
if status != MessageStatus.REJECTED:
165165
if print_message:

src/aleph_client/commands/files.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ async def pin(
5555
ref=ref,
5656
)
5757
logger.debug("Upload finished")
58-
typer.echo(f"{result.json(indent=4)}")
58+
typer.echo(f"{result.model_dump_json(indent=4)}")
5959

6060

6161
@app.command()
@@ -96,7 +96,7 @@ async def upload(
9696
ref=ref,
9797
)
9898
logger.debug("Upload finished")
99-
typer.echo(f"{result.json(indent=4)}")
99+
typer.echo(f"{result.model_dump_json(indent=4)}")
100100

101101

102102
@app.command()
@@ -170,7 +170,7 @@ async def forget(
170170

171171
async with AuthenticatedAlephHttpClient(account=account, api_server=settings.API_HOST) as client:
172172
value = await client.forget(hashes=hashes, reason=reason, channel=channel)
173-
typer.echo(f"{value[0].json(indent=4)}")
173+
typer.echo(f"{value[0].model_dump_json(indent=4)}")
174174

175175

176176
class GetAccountFilesQueryParams(BaseModel):
@@ -264,7 +264,7 @@ async def list_files(
264264

265265
uri = f"{settings.API_HOST}/api/v0/addresses/{address}/files"
266266
async with aiohttp.ClientSession() as session:
267-
response = await session.get(uri, params=query_params.dict())
267+
response = await session.get(uri, params=query_params.model_dump())
268268
if response.status == 200:
269269
files_data = await response.json()
270270
formatted_files_data = json_lib.dumps(files_data, indent=4)

src/aleph_client/commands/instance/__init__.py

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@
7171
filter_only_valid_messages,
7272
find_sevctl_or_exit,
7373
found_gpus_by_model,
74+
get_annotated_constraint,
7475
get_or_prompt_volumes,
7576
setup_logging,
7677
str_to_datetime,
@@ -91,6 +92,7 @@
9192
metavar_valid_chains = f"[{'|'.join(hold_chains)}]"
9293
super_token_chains = get_chains_with_super_token()
9394
metavar_valid_payg_chains = f"[{'|'.join(super_token_chains)}]"
95+
max_persistent_volume_size = get_annotated_constraint(PersistentVolumeSizeMib, "le")
9496

9597

9698
@app.command()
@@ -119,7 +121,7 @@ async def create(
119121
vcpus: Annotated[Optional[int], typer.Option(help=help_strings.VCPUS)] = None,
120122
memory: Annotated[Optional[int], typer.Option(help=help_strings.MEMORY)] = None,
121123
rootfs_size: Annotated[
122-
Optional[int], typer.Option(help=help_strings.ROOTFS_SIZE, max=PersistentVolumeSizeMib.le)
124+
Optional[int], typer.Option(help=help_strings.ROOTFS_SIZE, max=max_persistent_volume_size)
123125
] = None,
124126
timeout_seconds: Annotated[float, typer.Option(help=help_strings.TIMEOUT_SECONDS)] = settings.DEFAULT_VM_TIMEOUT,
125127
ssh_pubkey_file: Annotated[Path, typer.Option(help=help_strings.SSH_PUBKEY_FILE)] = Path(
@@ -341,7 +343,10 @@ async def create(
341343
disk_size_info = f"Rootfs Size: {round(disk_size/1024, 2)} GiB (defaulted to included storage in tier)"
342344
if not isinstance(rootfs_size, int):
343345
rootfs_size = validated_int_prompt(
344-
"Custom Rootfs Size (MiB)", min_value=disk_size, max_value=PersistentVolumeSizeMib.le, default=disk_size
346+
"Custom Rootfs Size (MiB)",
347+
min_value=disk_size,
348+
max_value=max_persistent_volume_size,
349+
default=disk_size,
345350
)
346351
if rootfs_size > disk_size:
347352
disk_size = rootfs_size
@@ -551,7 +556,7 @@ async def create(
551556
echo(f"Instance creation failed:\n{e}")
552557
raise typer.Exit(code=1) from e
553558
if print_message:
554-
echo(f"{message.json(indent=4)}")
559+
echo(f"{message.model_dump_json(indent=4)}")
555560

556561
item_hash: ItemHash = message.item_hash
557562
infos = []
@@ -796,7 +801,7 @@ async def delete(
796801

797802
message, status = await client.forget(hashes=[ItemHash(item_hash)], reason=reason)
798803
if print_message:
799-
echo(f"{message.json(indent=4)}")
804+
echo(f"{message.model_dump_json(indent=4)}")
800805
echo(f"Instance {item_hash} has been deleted.")
801806

802807

@@ -1018,7 +1023,7 @@ async def list_instances(
10181023
raise typer.Exit(code=1)
10191024
if json:
10201025
for message in messages:
1021-
echo(message.json(indent=4))
1026+
echo(message.model_dump_json(indent=4))
10221027
else:
10231028
# Since we filtered on message type, we can safely cast as InstanceMessage.
10241029
messages = cast(builtins.list[InstanceMessage], messages)
@@ -1197,7 +1202,6 @@ async def confidential_init_session(
11971202

11981203
# Generate sessions certificate files
11991204
if not ((session_dir / "vm_godh.b64").exists() and keep_session):
1200-
12011205
code, platform_file = await client.get_certificates()
12021206
if code != 200:
12031207
echo(
@@ -1368,7 +1372,7 @@ async def confidential_create(
13681372
vcpus: Annotated[Optional[int], typer.Option(help=help_strings.VCPUS)] = None,
13691373
memory: Annotated[Optional[int], typer.Option(help=help_strings.MEMORY)] = None,
13701374
rootfs_size: Annotated[
1371-
Optional[int], typer.Option(help=help_strings.ROOTFS_SIZE, max=PersistentVolumeSizeMib.le)
1375+
Optional[int], typer.Option(help=help_strings.ROOTFS_SIZE, max=max_persistent_volume_size)
13721376
] = None,
13731377
timeout_seconds: Annotated[float, typer.Option(help=help_strings.TIMEOUT_SECONDS)] = settings.DEFAULT_VM_TIMEOUT,
13741378
ssh_pubkey_file: Annotated[Path, typer.Option(help=help_strings.SSH_PUBKEY_FILE)] = Path(
@@ -1516,7 +1520,7 @@ async def gpu_create(
15161520
vcpus: Annotated[Optional[int], typer.Option(help=help_strings.VCPUS)] = None,
15171521
memory: Annotated[Optional[int], typer.Option(help=help_strings.MEMORY)] = None,
15181522
rootfs_size: Annotated[
1519-
Optional[int], typer.Option(help=help_strings.ROOTFS_SIZE, max=PersistentVolumeSizeMib.le)
1523+
Optional[int], typer.Option(help=help_strings.ROOTFS_SIZE, max=max_persistent_volume_size)
15201524
] = None,
15211525
premium: Annotated[Optional[bool], typer.Option(help=help_strings.GPU_PREMIUM_OPTION)] = None,
15221526
timeout_seconds: Annotated[float, typer.Option(help=help_strings.TIMEOUT_SECONDS)] = settings.DEFAULT_VM_TIMEOUT,

src/aleph_client/commands/message.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
from aleph_client.commands import help_strings
2727
from aleph_client.commands.utils import (
2828
colorful_json,
29-
colorful_message_json,
3029
colorized_status,
3130
input_multiline,
3231
setup_logging,
@@ -55,7 +54,7 @@ async def get(
5554
reason = await client.get_message_error(item_hash=ItemHash(item_hash))
5655
typer.echo(colorful_json(json.dumps(reason, indent=4)))
5756
else:
58-
typer.echo(colorful_message_json(message))
57+
typer.echo(colorful_json(json.dumps(message.model_dump(), indent=4, default=extended_json_encoder)))
5958

6059

6160
@app.command()
@@ -109,7 +108,9 @@ async def find(
109108
),
110109
ignore_invalid_messages=ignore_invalid_messages,
111110
)
112-
typer.echo(colorful_json(response.json(sort_keys=True, indent=4)))
111+
typer.echo(
112+
colorful_json(json.dumps(response.model_dump(), sort_keys=True, indent=4, default=extended_json_encoder))
113+
)
113114

114115

115116
@app.command()
@@ -165,7 +166,7 @@ async def post(
165166
storage_engine=storage_engine,
166167
)
167168

168-
typer.echo(json.dumps(result.dict(), indent=4, default=extended_json_encoder))
169+
typer.echo(json.dumps(result.model_dump(), indent=4, default=extended_json_encoder))
169170

170171

171172
@app.command()
@@ -195,7 +196,7 @@ async def amend(
195196
editor: str = os.getenv("EDITOR", default="nano")
196197
with tempfile.NamedTemporaryFile(suffix="json") as fd:
197198
# Fill in message template
198-
fd.write(existing_message.content.json(indent=4).encode())
199+
fd.write(existing_message.content.model_dump_json(indent=4).encode())
199200
fd.seek(0)
200201

201202
# Launch editor
@@ -224,7 +225,7 @@ async def amend(
224225
message_type=existing_message.type,
225226
channel=existing_message.channel,
226227
)
227-
typer.echo(f"{message.json(indent=4)}")
228+
typer.echo(f"{message.model_dump_json(indent=4)}")
228229

229230

230231
@app.command()
@@ -273,7 +274,7 @@ async def watch(
273274
async for message in client.watch_messages(
274275
message_filter=MessageFilter(refs=[ref], addresses=[original.content.address])
275276
):
276-
typer.echo(f"{message.json(indent=indent)}")
277+
typer.echo(f"{message.model_dump_json(indent=indent)}")
277278

278279

279280
@app.command()

src/aleph_client/commands/program.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ async def upload(
166166
)
167167
logger.debug("Code upload finished")
168168
if print_messages or print_code_message:
169-
typer.echo(f"{user_code.json(indent=4)}")
169+
typer.echo(f"{user_code.model_dump_json(indent=4)}")
170170
program_ref = user_code.item_hash
171171

172172
pricing = await fetch_pricing()
@@ -264,7 +264,7 @@ async def upload(
264264

265265
logger.debug("Program upload finished")
266266
if print_messages or print_program_message:
267-
typer.echo(f"{message.json(indent=4)}")
267+
typer.echo(f"{message.model_dump_json(indent=4)}")
268268

269269
item_hash: ItemHash = message.item_hash
270270
if verbose:
@@ -378,7 +378,7 @@ async def update(
378378
)
379379
logger.debug("Code upload finished")
380380
if print_message:
381-
typer.echo(f"{message.json(indent=4)}")
381+
typer.echo(f"{message.model_dump_json(indent=4)}")
382382

383383
if verbose:
384384
hash_base32 = b32encode(b16decode(item_hash.upper())).strip(b"=").lower().decode()
@@ -681,12 +681,12 @@ async def persist(
681681
return None
682682

683683
# Update content
684-
content: ProgramContent = message.content.copy()
684+
content: ProgramContent = message.content.model_copy()
685685
content.on.persistent = True
686686
content.replaces = message.item_hash
687687

688688
message, _status, _ = await client.submit(
689-
content=content.dict(exclude_none=True),
689+
content=content.model_dump(exclude_none=True),
690690
message_type=message.type,
691691
channel=message.channel,
692692
)
@@ -778,12 +778,12 @@ async def unpersist(
778778
return None
779779

780780
# Update content
781-
content: ProgramContent = message.content.copy()
781+
content: ProgramContent = message.content.model_copy()
782782
content.on.persistent = False
783783
content.replaces = message.item_hash
784784

785785
message, _status, _ = await client.submit(
786-
content=content.dict(exclude_none=True),
786+
content=content.model_dump(exclude_none=True),
787787
message_type=message.type,
788788
channel=message.channel,
789789
)

0 commit comments

Comments
 (0)