Skip to content

Commit cafb49f

Browse files
fix code
1 parent 5d76e4d commit cafb49f

File tree

9 files changed

+56
-58
lines changed

9 files changed

+56
-58
lines changed

services/invitations/src/simcore_service_invitations/cli.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from cryptography.fernet import Fernet
66
from models_library.emails import LowerCaseEmailStr
77
from models_library.invitations import InvitationContent, InvitationInputs
8-
from pydantic import EmailStr, HttpUrl, ValidationError, parse_obj_as
8+
from pydantic import EmailStr, HttpUrl, TypeAdapter, ValidationError
99
from rich.console import Console
1010
from servicelib.utils_secrets import generate_password
1111
from settings_library.utils_cli import (
@@ -96,19 +96,19 @@ def invite(
9696
ctx: typer.Context,
9797
email: str = typer.Argument(
9898
...,
99-
callback=lambda v: parse_obj_as(LowerCaseEmailStr, v),
99+
callback=lambda v: TypeAdapter(LowerCaseEmailStr).validate_python(v),
100100
help="Custom invitation for a given guest",
101101
),
102102
issuer: str = typer.Option(
103-
..., help=InvitationInputs.__fields__["issuer"].field_info.description
103+
..., help=InvitationInputs.model_fields["issuer"].description
104104
),
105105
trial_account_days: int = typer.Option(
106106
None,
107-
help=InvitationInputs.__fields__["trial_account_days"].field_info.description,
107+
help=InvitationInputs.model_fields["trial_account_days"].description,
108108
),
109109
product: str = typer.Option(
110110
None,
111-
help=InvitationInputs.__fields__["product"].field_info.description,
111+
help=InvitationInputs.model_fields["product"].description,
112112
),
113113
):
114114
"""Creates an invitation link for user with 'email' and issued by 'issuer'"""
@@ -117,7 +117,7 @@ def invite(
117117

118118
invitation_data = InvitationInputs(
119119
issuer=issuer,
120-
guest=parse_obj_as(EmailStr, email),
120+
guest=TypeAdapter(EmailStr).validate_python(email),
121121
trial_account_days=trial_account_days,
122122
extra_credits_in_usd=None,
123123
product=product,
@@ -142,14 +142,14 @@ def extract(ctx: typer.Context, invitation_url: str):
142142
try:
143143
invitation: InvitationContent = extract_invitation_content(
144144
invitation_code=extract_invitation_code_from_query(
145-
parse_obj_as(HttpUrl, invitation_url)
145+
TypeAdapter(HttpUrl).validate_python(invitation_url)
146146
),
147147
secret_key=settings.INVITATIONS_SECRET_KEY.get_secret_value().encode(),
148148
default_product=settings.INVITATIONS_DEFAULT_PRODUCT,
149149
)
150150
assert invitation.product is not None # nosec
151151

152-
print(invitation.json(indent=1)) # noqa: T201
152+
print(invitation.model_dump_json(indent=1)) # noqa: T201
153153

154154
except (InvalidInvitationCodeError, ValidationError):
155155
_err_console.print("[bold red]Invalid code[/bold red]")

services/invitations/src/simcore_service_invitations/core/settings.py

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
11
from functools import cached_property
22

33
from models_library.products import ProductName
4-
from pydantic import Field, HttpUrl, PositiveInt, SecretStr, validator
4+
from pydantic import (
5+
AliasChoices,
6+
Field,
7+
HttpUrl,
8+
PositiveInt,
9+
SecretStr,
10+
field_validator,
11+
)
512
from settings_library.base import BaseCustomSettings
613
from settings_library.basic_types import BuildTargetEnum, LogLevel, VersionTag
714
from settings_library.utils_logging import MixinLoggingSettings
@@ -38,22 +45,23 @@ class _BaseApplicationSettings(BaseCustomSettings, MixinLoggingSettings):
3845
# RUNTIME -----------------------------------------------------------
3946

4047
INVITATIONS_LOGLEVEL: LogLevel = Field(
41-
default=LogLevel.INFO, env=["INVITATIONS_LOGLEVEL", "LOG_LEVEL", "LOGLEVEL"]
48+
default=LogLevel.INFO,
49+
validation_alias=AliasChoices("INVITATIONS_LOGLEVEL", "LOG_LEVEL", "LOGLEVEL"),
4250
)
4351
INVITATIONS_LOG_FORMAT_LOCAL_DEV_ENABLED: bool = Field(
4452
default=False,
45-
env=[
53+
validation_alias=AliasChoices(
4654
"INVITATIONS_LOG_FORMAT_LOCAL_DEV_ENABLED",
4755
"LOG_FORMAT_LOCAL_DEV_ENABLED",
48-
],
56+
),
4957
description="Enables local development log format. WARNING: make sure it is disabled if you want to have structured logs!",
5058
)
5159

5260
@cached_property
5361
def LOG_LEVEL(self):
5462
return self.INVITATIONS_LOGLEVEL
5563

56-
@validator("INVITATIONS_LOGLEVEL")
64+
@field_validator("INVITATIONS_LOGLEVEL")
5765
@classmethod
5866
def valid_log_level(cls, value: str) -> str:
5967
return cls.validate_log_level(value)

services/invitations/src/simcore_service_invitations/services/invitations.py

Lines changed: 21 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,22 @@
11
import base64
22
import binascii
33
import logging
4-
from typing import Any, ClassVar, cast
54
from urllib import parse
65

6+
from common_library.pydantic_networks_extension import HttpUrlLegacy
77
from cryptography.fernet import Fernet, InvalidToken
88
from models_library.invitations import InvitationContent, InvitationInputs
99
from models_library.products import ProductName
10-
from pydantic import HttpUrl, ValidationError, parse_obj_as
10+
from pydantic import ConfigDict, HttpUrl, TypeAdapter, ValidationError
1111
from starlette.datastructures import URL
1212

1313
_logger = logging.getLogger(__name__)
1414

1515

16+
def _to_initial(v: str):
17+
return v[0]
18+
19+
1620
class InvalidInvitationCodeError(Exception):
1721
...
1822

@@ -23,9 +27,9 @@ class _ContentWithShortNames(InvitationContent):
2327
@classmethod
2428
def serialize(cls, model_obj: InvitationContent) -> str:
2529
"""Exports to json using *short* aliases and values in order to produce shorter codes"""
26-
model_w_short_aliases_json: str = cls.construct(
27-
**model_obj.dict(exclude_unset=True)
28-
).json(exclude_unset=True, by_alias=True)
30+
model_w_short_aliases_json: str = cls.model_construct(
31+
**model_obj.model_dump(exclude_unset=True)
32+
).model_dump_json(exclude_unset=True, by_alias=True)
2933
# NOTE: json arguments try to minimize the amount of data
3034
# serialized. The CONS is that it relies on models in the code
3135
# that might change over time. This might lead to some datasets in codes
@@ -35,36 +39,18 @@ def serialize(cls, model_obj: InvitationContent) -> str:
3539
@classmethod
3640
def deserialize(cls, raw_json: str) -> InvitationContent:
3741
"""Parses a json string and returns InvitationContent model"""
38-
model_w_short_aliases = cls.parse_raw(raw_json)
39-
return InvitationContent.construct(
40-
**model_w_short_aliases.dict(exclude_unset=True)
42+
model_w_short_aliases = cls.model_validate_json(raw_json)
43+
return InvitationContent.model_construct(
44+
**model_w_short_aliases.model_dump(exclude_unset=True)
4145
)
4246

43-
class Config:
44-
allow_population_by_field_name = True # NOTE: can parse using field names
45-
allow_mutation = False
46-
anystr_strip_whitespace = True
47+
model_config = ConfigDict(
4748
# NOTE: Can export with alias: short aliases to minimize the size of serialization artifact
48-
fields: ClassVar[dict[str, Any]] = {
49-
"issuer": {
50-
"alias": "i",
51-
},
52-
"guest": {
53-
"alias": "g",
54-
},
55-
"trial_account_days": {
56-
"alias": "t",
57-
},
58-
"extra_credits_in_usd": {
59-
"alias": "e",
60-
},
61-
"product": {
62-
"alias": "p",
63-
},
64-
"created": {
65-
"alias": "c",
66-
},
67-
}
49+
alias_generator=_to_initial,
50+
populate_by_name=True, # NOTE: can parse using field names
51+
frozen=True,
52+
str_strip_whitespace=True,
53+
)
6854

6955

7056
#
@@ -79,9 +65,9 @@ def _build_link(
7965
r = URL("/registration").include_query_params(invitation=code_url_safe)
8066

8167
# Adds query to fragment
82-
base_url = f"{base_url.rstrip('/')}/"
68+
base_url = f"{base_url}/"
8369
url = URL(base_url).replace(fragment=f"{r}")
84-
return cast(HttpUrl, parse_obj_as(HttpUrl, f"{url}"))
70+
return TypeAdapter(HttpUrlLegacy).validate_python(f"{url}")
8571

8672

8773
def _fernet_encrypt_as_urlsafe_code(
@@ -124,7 +110,7 @@ def create_invitation_link_and_content(
124110
code = _create_invitation_code(content, secret_key)
125111
# Adds message as the invitation in query
126112
link = _build_link(
127-
base_url=base_url,
113+
base_url=f"{base_url}",
128114
code_url_safe=code.decode(),
129115
)
130116
return link, content

services/invitations/tests/unit/api/conftest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ def client(app_environment: EnvVarsDict) -> Iterator[TestClient]:
1818
print(f"app_environment={json.dumps(app_environment)}")
1919

2020
app = create_app()
21-
print("settings:\n", app.state.settings.json(indent=1))
21+
print("settings:\n", app.state.settings.model_dump_json(indent=1))
2222
with TestClient(app, base_url="http://testserver.test") as client:
2323
yield client
2424

services/invitations/tests/unit/api/test_api_invitations.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ def test_check_invitation(
6464

6565
# up ot here, identifcal to above.
6666
# Let's use invitation link
67-
invitation_url = ApiInvitationContentAndLink.parse_obj(
67+
invitation_url = ApiInvitationContentAndLink.model_validate(
6868
response.json()
6969
).invitation_url
7070

@@ -77,7 +77,7 @@ def test_check_invitation(
7777
assert response.status_code == 200, f"{response.json()=}"
7878

7979
# decrypted invitation should be identical to request above
80-
invitation = InvitationContent.parse_obj(response.json())
80+
invitation = InvitationContent.model_validate(response.json())
8181
assert invitation.issuer == invitation_data.issuer
8282
assert invitation.guest == invitation_data.guest
8383
assert invitation.trial_account_days == invitation_data.trial_account_days
@@ -106,7 +106,7 @@ def test_check_valid_invitation(
106106
assert response.status_code == 200, f"{response.json()=}"
107107

108108
# decrypted invitation should be identical to request above
109-
invitation = InvitationContent.parse_obj(response.json())
109+
invitation = InvitationContent.model_validate(response.json())
110110

111111
assert invitation.issuer == invitation_data.issuer
112112
assert invitation.guest == invitation_data.guest

services/invitations/tests/unit/api/test_api_meta.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ def test_healthcheck(client: TestClient):
1919
def test_meta(client: TestClient):
2020
response = client.get(f"/{API_VTAG}/meta")
2121
assert response.status_code == status.HTTP_200_OK
22-
meta = _Meta.parse_obj(response.json())
22+
meta = _Meta.model_validate(response.json())
2323

2424
response = client.get(meta.docs_url)
2525
assert response.status_code == status.HTTP_200_OK

services/invitations/tests/unit/conftest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,4 +109,4 @@ def invitation_data(
109109
if product:
110110
kwargs["product"] = product
111111

112-
return InvitationInputs.parse_obj(kwargs)
112+
return InvitationInputs.model_validate(kwargs)

services/invitations/tests/unit/test__model_examples.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,4 @@ def test_model_examples(
2626
model_cls: type[BaseModel], example_name: int, example_data: Any
2727
):
2828
print(example_name, ":", json.dumps(example_data))
29-
assert model_cls.parse_obj(example_data)
29+
assert model_cls.model_validate(example_data)

services/invitations/tests/unit/test_cli.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import pytest
99
from faker import Faker
1010
from models_library.products import ProductName
11+
from pydantic import TypeAdapter
1112
from pytest_simcore.helpers.monkeypatch_envs import load_dotenv, setenvs_from_dict
1213
from pytest_simcore.helpers.typing_env import EnvVarsDict
1314
from simcore_service_invitations._meta import API_VERSION
@@ -71,7 +72,10 @@ def test_invite_user_and_check_invitation(
7172
env=environs,
7273
)
7374
assert result.exit_code == os.EX_OK, result.output
74-
assert expected == InvitationInputs.parse_raw(result.stdout).dict()
75+
assert (
76+
expected
77+
== TypeAdapter(InvitationInputs).validate_json(result.stdout).model_dump()
78+
)
7579

7680

7781
def test_echo_dotenv(cli_runner: CliRunner, monkeypatch: pytest.MonkeyPatch):
@@ -82,7 +86,7 @@ def test_echo_dotenv(cli_runner: CliRunner, monkeypatch: pytest.MonkeyPatch):
8286
environs = load_dotenv(result.stdout)
8387

8488
envs = setenvs_from_dict(monkeypatch, environs)
85-
settings_from_obj = ApplicationSettings.parse_obj(envs)
89+
settings_from_obj = ApplicationSettings.model_validate(envs)
8690
settings_from_envs = ApplicationSettings.create_from_envs()
8791

8892
assert settings_from_envs == settings_from_obj
@@ -93,5 +97,5 @@ def test_list_settings(cli_runner: CliRunner, app_environment: EnvVarsDict):
9397
assert result.exit_code == os.EX_OK, result.output
9498

9599
print(result.output)
96-
settings = ApplicationSettings.parse_raw(result.output)
100+
settings = TypeAdapter(ApplicationSettings).validate_json(result.output)
97101
assert settings == ApplicationSettings.create_from_envs()

0 commit comments

Comments
 (0)