Merge pull request #22463 from mvdbeek/warnings/pydantic-v2-mechanical

Warnings/pydantic v2 cleanup
This commit is contained in:
Marius van den Beek
2026-04-17 14:50:14 +02:00
committed by GitHub
33 changed files with 193 additions and 92 deletions
+5 -5
View File
@@ -8459,8 +8459,8 @@ export interface components {
/** CollectionElementCollectionRequestUri */
CollectionElementCollectionRequestUri: {
/**
* Class
* @constant
* @description discriminator enum property added by openapi-typescript
* @enum {string}
*/
class: "Collection";
/** Collection Type */
@@ -8479,8 +8479,8 @@ export interface components {
/** CollectionElementDataRequestUri */
CollectionElementDataRequestUri: {
/**
* Class
* @constant
* @description discriminator enum property added by openapi-typescript
* @enum {string}
*/
class: "File";
/** Created From Basename */
@@ -20712,7 +20712,7 @@ export interface components {
/** Dry Run */
dry_run: boolean;
/** Workflow */
workflow: string;
workflow: unknown;
};
/** RegexJobMessage */
RegexJobMessage: {
+1 -1
View File
@@ -203,7 +203,7 @@ text-unidecode==1.3
tinydb==4.8.2
toml==0.10.2
tomli==2.4.1 ; python_full_version <= '3.11'
total-perspective-vortex==3.2.0
total-perspective-vortex==3.2.1
trio==0.33.0
trio-websocket==0.12.2
tuspy==1.1.0
@@ -95,7 +95,7 @@ google-auth==2.49.2
google-cloud-batch==0.21.0
google-genai==1.72.0
googleapis-common-protos==1.74.0
gravity==1.2.0
gravity==1.2.1
greenlet==3.4.0 ; platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64'
griffelib==2.0.2
groq==1.1.2
@@ -206,7 +206,7 @@ pydantic-evals==1.80.0
pydantic-graph==1.80.0
pydantic-handlebars==0.1.0
pydantic-settings==2.13.1
pydantic-tes==0.3.0
pydantic-tes==0.4.0
pydicom==3.0.2
pydot==4.0.1
pyeventsystem==0.1.0
@@ -155,7 +155,7 @@ text-unidecode==1.3
tinydb==4.8.2
toml==0.10.2
tomli==2.4.1 ; python_full_version <= '3.11'
total-perspective-vortex==3.2.0
total-perspective-vortex==3.2.1
trio==0.33.0
trio-websocket==0.12.2
tuspy==1.1.0
+10 -16
View File
@@ -33,7 +33,10 @@ from galaxy.model import (
DatasetPermissions,
HistoryDatasetAssociation,
)
from galaxy.model.db.role import get_private_role_user_emails_dict
from galaxy.model.db.role import (
get_private_role_user_emails_dict,
role_name_id_pairs,
)
from galaxy.schema.tasks import (
ComputeDatasetHashTaskRequest,
PurgeDatasetsTaskRequest,
@@ -472,23 +475,14 @@ class DatasetAssociationManager(
)
all_role_ids = {r.id for r in access_roles | manage_roles | modify_roles}
private_role_emails = get_private_role_user_emails_dict(self.session(), role_ids=all_role_ids)
encode_id = self.app.security.encode_id
def make_tuples(roles: set):
tuples = []
for role in roles:
# use role name for non-private roles, and user.email from private rules
displayed_name = private_role_emails.get(role.id, role.name)
role_tuple = (displayed_name, self.app.security.encode_id(role.id))
tuples.append(role_tuple)
return tuples
access_dataset_role_list = make_tuples(access_roles)
manage_dataset_role_list = make_tuples(manage_roles)
rval = dict(access_dataset_roles=access_dataset_role_list, manage_dataset_roles=manage_dataset_role_list)
rval = dict(
access_dataset_roles=role_name_id_pairs(access_roles, private_role_emails, encode_id),
manage_dataset_roles=role_name_id_pairs(manage_roles, private_role_emails, encode_id),
)
if library_dataset is not None:
modify_item_role_list = make_tuples(modify_roles)
rval["modify_item_roles"] = modify_item_role_list
rval["modify_item_roles"] = role_name_id_pairs(modify_roles, private_role_emails, encode_id)
return rval
def ensure_dataset_on_disk(self, trans, dataset: U):
+3 -3
View File
@@ -3,7 +3,7 @@ from typing import (
Union,
)
from pydantic.tools import parse_obj_as
from pydantic import TypeAdapter
from galaxy.datatypes._schema import (
DatatypeConverterList,
@@ -87,7 +87,7 @@ def view_converters(datatypes_registry: Registry) -> DatatypeConverterList:
"tool_id": targets[target_type].id,
}
)
return parse_obj_as(DatatypeConverterList, converters)
return TypeAdapter(DatatypeConverterList).validate_python(converters)
def _get_edam_details(datatypes_registry: Registry, edam_ids: dict[str, str]) -> dict[str, dict]:
@@ -158,7 +158,7 @@ def view_visualization_mappings(
}
)
return parse_obj_as(DatatypeVisualizationMappingsList, mappings)
return TypeAdapter(DatatypeVisualizationMappingsList).validate_python(mappings)
def get_preferred_visualization(datatypes_registry: Registry, datatype_extension: str) -> Optional[dict[str, str]]:
+8 -14
View File
@@ -47,7 +47,10 @@ from galaxy.model import (
LibraryFolder,
LibraryFolderPermissions,
)
from galaxy.model.db.role import get_private_role_user_emails_dict
from galaxy.model.db.role import (
get_private_role_user_emails_dict,
role_name_id_pairs,
)
from galaxy.model.scoped_session import galaxy_scoped_session
from galaxy.schema.schema import LibraryFolderContentsIndexQueryPayload
from galaxy.security import RBACAgent
@@ -324,20 +327,11 @@ class FolderManager:
)
all_role_ids = {r.id for r in modify_roles | manage_roles | add_roles}
private_role_emails = get_private_role_user_emails_dict(trans.sa_session, role_ids=all_role_ids)
def make_tuples(roles: set):
tuples = []
for role in roles:
# use role name for non-private roles, and user.email from private rules
displayed_name = private_role_emails.get(role.id, role.name)
role_tuple = (displayed_name, trans.security.encode_id(role.id))
tuples.append(role_tuple)
return tuples
encode_id = trans.security.encode_id
return dict(
modify_folder_role_list=make_tuples(modify_roles),
manage_folder_role_list=make_tuples(manage_roles),
add_library_item_role_list=make_tuples(add_roles),
modify_folder_role_list=role_name_id_pairs(modify_roles, private_role_emails, encode_id),
manage_folder_role_list=role_name_id_pairs(manage_roles, private_role_emails, encode_id),
add_library_item_role_list=role_name_id_pairs(add_roles, private_role_emails, encode_id),
)
def can_add_item(self, trans, folder):
+6
View File
@@ -15,6 +15,7 @@ from typing import (
TYPE_CHECKING,
Union,
)
from uuid import UUID
from sqlalchemy import (
asc,
@@ -718,6 +719,11 @@ class HistoryExportManager:
if "target_uri" in payload_raw:
payload = WriteStoreToPayload.model_construct(**payload_raw)
else:
# UUID field bypasses validation under model_construct, so coerce the
# stored string to UUID so Pydantic's serializer emits it cleanly.
request_id = payload_raw.get("short_term_storage_request_id")
if isinstance(request_id, str):
payload_raw["short_term_storage_request_id"] = UUID(request_id)
payload = ShortTermStoreExportPayload.model_construct(**payload_raw)
request_data = ExportObjectRequestMetadata.model_construct(
object_id=request_data_raw.get("object_id"),
+9 -15
View File
@@ -27,7 +27,10 @@ from galaxy.model.db.library import (
get_library_ids,
get_library_permissions_by_role,
)
from galaxy.model.db.role import get_private_role_user_emails_dict
from galaxy.model.db.role import (
get_private_role_user_emails_dict,
role_name_id_pairs,
)
from galaxy.util import (
pretty_print_time_interval,
unicodify,
@@ -278,21 +281,12 @@ class LibraryManager:
add_roles = self.get_add_roles(trans, library)
all_role_ids = {r.id for r in access_roles | modify_roles | manage_roles | add_roles}
private_role_emails = get_private_role_user_emails_dict(trans.sa_session, role_ids=all_role_ids)
def make_tuples(roles: set):
tuples = []
for role in roles:
# use role name for non-private roles, and user.email from private rules
displayed_name = private_role_emails.get(role.id, role.name)
role_tuple = (displayed_name, trans.security.encode_id(role.id))
tuples.append(role_tuple)
return tuples
encode_id = trans.security.encode_id
return dict(
access_library_role_list=make_tuples(access_roles),
modify_library_role_list=make_tuples(modify_roles),
manage_library_role_list=make_tuples(manage_roles),
add_library_item_role_list=make_tuples(add_roles),
access_library_role_list=role_name_id_pairs(access_roles, private_role_emails, encode_id),
modify_library_role_list=role_name_id_pairs(modify_roles, private_role_emails, encode_id),
manage_library_role_list=role_name_id_pairs(manage_roles, private_role_emails, encode_id),
add_library_item_role_list=role_name_id_pairs(add_roles, private_role_emails, encode_id),
)
def get_access_roles(self, trans, library: Library) -> set[Role]:
+1 -1
View File
@@ -395,7 +395,7 @@ class NotificationManager:
if request.expiration_time is not None:
stmt = stmt.values(expiration_time=request.expiration_time)
if request.content is not None:
stmt = stmt.values(content=request.content.json())
stmt = stmt.values(content=request.content.model_dump_json())
result = cast(CursorResult, self.sa_session.execute(stmt))
updated_row_count = result.rowcount
self.sa_session.commit()
+1 -1
View File
@@ -53,7 +53,7 @@ class QuotaManager:
return cast(DatabaseQuotaAgent, self.app.quota_agent)
def create_quota(self, payload: dict, decode_id=None) -> tuple[model.Quota, str]:
params = CreateQuotaParams.parse_obj(payload)
params = CreateQuotaParams.model_validate(payload)
create_amount = self._parse_amount(params.amount)
stmt = select(Quota).where(Quota.name == params.name).limit(1)
if self.sa_session.scalars(stmt).first():
+1 -1
View File
@@ -2394,7 +2394,7 @@ class WorkflowContentsManager(UsesAnnotations):
return sa_session.execute(stmnt.order_by(model.StoredWorkflow.id.desc()).limit(1)).scalar()
def safe_wraps(v: Any, nxt: SerializerFunctionWrapHandler) -> str:
def safe_wraps(v: Any, nxt: SerializerFunctionWrapHandler) -> Any:
try:
return nxt(v)
except Exception:
+1 -1
View File
@@ -10230,7 +10230,7 @@ class WorkflowInvocation(Base, UsesCreateAndUpdateTime, Dictifiable, Serializabl
def add_message(self, message: "InvocationMessageUnion"):
message_dict = message.dict(
message_dict = message.model_dump(
exclude_unset=True,
exclude={"history_id"}, # history_id comes in through workflow_invocation and isn't persisted in database
)
+20
View File
@@ -1,3 +1,8 @@
from collections.abc import (
Callable,
Iterable,
)
from sqlalchemy import (
and_,
false,
@@ -99,3 +104,18 @@ def get_private_role_user_emails_dict(session, role_ids: set[int] | None = None)
stmt = stmt.where(UserRoleAssociation.role_id.in_(role_ids))
roleid_email_tuples = session.execute(stmt).all()
return dict(roleid_email_tuples)
def role_name_id_pairs(
roles: Iterable[Role],
private_role_emails: dict[int, str],
encode_id: Callable[[int], str],
) -> list[list[str]]:
"""Build [displayed_name, encoded_id] pairs for a set of roles.
Private roles render as the owning user's email (looked up via
``private_role_emails``); other roles render as their role name.
The return shape matches RoleNameIdTuple in the API schema, which
is declared as list[str] (a 2-element array on the wire).
"""
return [[private_role_emails.get(role.id, role.name), encode_id(role.id)] for role in roles]
+1 -1
View File
@@ -348,7 +348,7 @@ class NotificationCreatedResponse(Model):
class NotificationUpdateRequest(Model):
def has_changes(self) -> bool:
"""Whether the notification update request contains at least one change."""
return any(getattr(self, field) is not None for field in self.__fields__.keys())
return any(getattr(self, field) is not None for field in type(self).model_fields)
class UserNotificationUpdateRequest(NotificationUpdateRequest):
+17 -1
View File
@@ -22,11 +22,13 @@ from pydantic import (
BaseModel,
BeforeValidator,
ConfigDict,
Discriminator,
Field,
HttpUrl,
Json,
model_validator,
RootModel,
Tag,
UUID4,
)
from pydantic_core import core_schema
@@ -1979,11 +1981,25 @@ class ExportObjectType(str, Enum):
INVOCATION = "invocation"
def _store_export_payload_discriminator(value: Any) -> str:
# Route untagged persisted/constructed payloads to the right union branch
# without requiring a tag field on the payload classes themselves.
if isinstance(value, dict):
return "short_term" if "short_term_storage_request_id" in value else "write"
return "short_term" if isinstance(value, ShortTermStoreExportPayload) else "write"
class ExportObjectRequestMetadata(Model):
object_id: EncodedDatabaseIdField
object_type: ExportObjectType
user_id: Optional[EncodedDatabaseIdField] = None
payload: Union[WriteStoreToPayload, ShortTermStoreExportPayload]
payload: Annotated[
Union[
Annotated[WriteStoreToPayload, Tag("write")],
Annotated[ShortTermStoreExportPayload, Tag("short_term")],
],
Discriminator(_store_export_payload_discriminator),
]
class ExportObjectResultMetadata(Model):
+1 -1
View File
@@ -871,7 +871,7 @@ class TabularToolDataField(Dictifiable):
rval = super().to_dict(view, value_mapper)
rval["name"] = self.data["value"]
rval["fields"] = self.data
rval["base_dir"] = (self.get_base_dir(),)
rval["base_dir"] = [self.get_base_dir()]
rval["files"] = self.get_filesize_map(True)
rval["fingerprint"] = self.get_fingerprint()
return rval
@@ -472,7 +472,7 @@ class ToolOutputCollectionStructure:
return collection_prototype
def to_dict(self):
return self.to_model().dict()
return self.to_model().model_dump()
def to_model(self) -> ToolOutputCollectionStructureModel:
discover_datasets = []
+1 -1
View File
@@ -145,7 +145,7 @@ def _description_from_tool_source(
request_schema: Optional[Dict[str, Any]] = None
if request_and_schema:
request = request_and_schema.request.input_state
request_schema = request_and_schema.request_schema.dict()
request_schema = request_and_schema.request_schema.model_dump()
value_state_representation = raw_test_dict.get("value_state_representation", "test_case_xml")
tool_id, tool_version = _tool_id_and_version(tool_source, tool_guid)
+32 -11
View File
@@ -609,12 +609,7 @@ class CollectionElementCollectionRequestUri(StrictModel):
validation_alias=AliasChoices("identifier", "name"),
)
collection_type: StrictStr
elements: List[
Annotated[
Union["CollectionElementCollectionRequestUri", CollectionElementDataRequestUri],
Field(discriminator="class_"),
]
]
elements: List["CollectionRequestUriElement"]
@model_validator(mode="before")
@classmethod
@@ -628,14 +623,39 @@ class CollectionElementCollectionRequestUri(StrictModel):
return data
def _collection_element_discriminator(value: Any) -> Optional[str]:
if isinstance(value, dict):
return value.get("class") or value.get("class_")
return getattr(value, "class_", None)
# A callable Discriminator avoids the PydanticJsonSchemaWarning emitted for
# the recursive Field(discriminator="class_") on this self-referential union;
# json_schema_extra restores the OpenAPI discriminator metadata.
CollectionRequestUriElement = Annotated[
Union[
Annotated[CollectionElementCollectionRequestUri, Tag("Collection")],
Annotated[CollectionElementDataRequestUri, Tag("File")],
],
Discriminator(_collection_element_discriminator),
Field(
json_schema_extra={
"discriminator": {
"propertyName": "class",
"mapping": {
"Collection": "#/components/schemas/CollectionElementCollectionRequestUri",
"File": "#/components/schemas/CollectionElementDataRequestUri",
},
}
}
),
]
class DataRequestCollectionUri(StrictModel):
class_: Literal["Collection"] = Field(..., alias="class")
collection_type: str
elements: List[
Annotated[
Union[CollectionElementCollectionRequestUri, CollectionElementDataRequestUri], Field(discriminator="class_")
]
]
elements: List[CollectionRequestUriElement]
deferred: StrictBool = False
name: Optional[StrictStr] = None
src: None = Field(None, exclude=True)
@@ -658,6 +678,7 @@ DataRequestLdda.model_rebuild()
DataRequestDce.model_rebuild()
DataRequestUri.model_rebuild()
DataRequestHdca.model_rebuild()
CollectionElementCollectionRequestUri.model_rebuild()
DataRequestCollectionUri.model_rebuild()
DataOrCollectionRequestAdapter: TypeAdapter[DataOrCollectionRequest] = TypeAdapter(DataOrCollectionRequest)
@@ -12,6 +12,10 @@ from typing import (
Union,
)
from pydantic import (
ConfigDict,
with_config,
)
from typing_extensions import (
Literal,
NotRequired,
@@ -31,6 +35,7 @@ SampleSheetColumnValueT = Union[int, float, bool, str, NoneType]
# type ignore because mypy can't handle closed TypedDicts yet
@with_config(ConfigDict(extra="forbid"))
class SampleSheetColumnDefinition(TypedDict, closed=True): # type: ignore[call-arg]
name: str
description: NotRequired[Optional[str]]
@@ -6,7 +6,9 @@ from typing import (
)
from pydantic import (
ConfigDict,
Field,
with_config,
)
from typing_extensions import (
Annotated,
@@ -173,6 +175,7 @@ FieldType = Union[CwlType, List[CwlType]]
# type ignore because mypy can't handle closed TypedDicts yet
@with_config(ConfigDict(extra="forbid"))
class FieldDict(TypedDict, closed=True): # type: ignore[call-arg]
name: str
type: FieldType
+3 -3
View File
@@ -2052,12 +2052,12 @@ def src_id_to_item(
for item in adapting:
element = TransientCollectionAdapterDatasetInstanceElement(
item.name,
cast(HistoryDatasetAssociation, src_id_to_item(sa_session, item.dict(), security)),
cast(HistoryDatasetAssociation, src_id_to_item(sa_session, item.model_dump(), security)),
)
elements.append(element)
return recover_adapter(elements, adapter_model)
else:
value = adapting.dict()
value = adapting.model_dump()
src_to_class = {
"hda": HistoryDatasetAssociation,
"ldda": LibraryDatasetDatasetAssociation,
@@ -2994,7 +2994,7 @@ def history_item_to_json(value, app, use_security):
collection_adapter: Optional[CollectionAdapter] = None
if isinstance(value, CollectionAdapter):
collection_adapter = value
return collection_adapter.to_adapter_model().dict()
return collection_adapter.to_adapter_model().model_dump()
if isinstance(value, MutableMapping) and "src" in value and "id" in value:
return value
elif isinstance(value, DatasetCollectionElement):
+2 -2
View File
@@ -6,7 +6,7 @@ import logging
import os
import yaml
from pydantic import parse_obj_as
from pydantic import TypeAdapter
from galaxy.exceptions import ObjectNotFound
from galaxy.navigation.data import load_root_component
@@ -106,7 +106,7 @@ class ToursRegistryImpl:
"requirements": self.tours[k].get("requirements"),
}
tours.append(tourdata)
return parse_obj_as(TourList, tours)
return TypeAdapter(TourList).validate_python(tours)
def tour_contents(self, tour_id):
"""Return tour contents."""
@@ -138,7 +138,15 @@ class FastAPIConfiguration:
def _user_to_model(user):
if user:
return UserModel.model_construct(
**user.to_dict(view="element", value_mapper={"id": Security.security.encode_id})
**user.to_dict(
view="element",
value_mapper={
"id": Security.security.encode_id,
# Dictifiable otherwise stringifies datetimes via isoformat(); keep the
# datetime object so UserModel.last_password_change serializes correctly.
"last_password_change": lambda v: v,
},
)
)
return None
+19 -1
View File
@@ -9,6 +9,7 @@ from typing import (
Optional,
Union,
)
from uuid import UUID
from fastapi import Query
@@ -20,6 +21,8 @@ from galaxy.schema.schema import (
ExportObjectResultMetadata,
ExportTaskListResponse,
ObjectExportTaskResponse,
ShortTermStoreExportPayload,
WriteStoreToPayload,
)
from galaxy.webapps.galaxy.api import (
depends,
@@ -105,11 +108,26 @@ class FastAPIExports:
request_data_raw = metadata.get("request_data", {})
result_data_raw = metadata.get("result_data")
payload_raw = request_data_raw.get("payload") or {}
# Pick the right payload flavour by presence of target_uri
# (WriteStoreToPayload has it, ShortTermStoreExportPayload does not).
if "target_uri" in payload_raw:
payload: Union[WriteStoreToPayload, ShortTermStoreExportPayload] = WriteStoreToPayload.model_construct(
**payload_raw
)
else:
# UUID field bypasses validation under model_construct, so coerce the
# stored string to UUID so Pydantic's serializer emits it cleanly.
request_id = payload_raw.get("short_term_storage_request_id")
if isinstance(request_id, str):
payload_raw["short_term_storage_request_id"] = UUID(request_id)
payload = ShortTermStoreExportPayload.model_construct(**payload_raw)
request_data = ExportObjectRequestMetadata.model_construct(
object_id=request_data_raw.get("object_id"),
object_type=request_data_raw.get("object_type"),
user_id=request_data_raw.get("user_id"),
payload=request_data_raw.get("payload"),
payload=payload,
)
result_data = None
+18 -1
View File
@@ -22,6 +22,10 @@ from fastapi import (
Response,
status,
)
from pydantic import (
Discriminator,
Tag,
)
from pydantic.fields import Field
from pydantic.main import BaseModel
@@ -186,6 +190,19 @@ IndexExportsAcceptHeader = Annotated[
]
def _index_exports_response_discriminator(value: Any) -> str:
return "tasks" if isinstance(value, ExportTaskListResponse) else "jobs"
IndexExportsResponse = Annotated[
Union[
Annotated[JobExportHistoryArchiveListResponse, Tag("jobs")],
Annotated[ExportTaskListResponse, Tag("tasks")],
],
Discriminator(_index_exports_response_discriminator),
]
@router.cbv
class FastAPIHistories:
service: HistoriesService = depends(HistoriesService)
@@ -551,7 +568,7 @@ class FastAPIHistories:
limit: Optional[int] = LimitQueryParam,
offset: Optional[int] = OffsetQueryParam,
accept: IndexExportsAcceptHeader = "application/json",
) -> Union[JobExportHistoryArchiveListResponse, ExportTaskListResponse]:
) -> IndexExportsResponse:
"""
By default the legacy job-based history exports (jeha) are returned.
@@ -182,7 +182,7 @@ class DatasetCollectionsService(ServiceBase, UsesLibraryMixinItems):
:returns: element view of new dataset collection
"""
# TODO: Error handling...
create_params = api_payload_to_create_params(payload.dict(exclude_unset=True, by_alias=True))
create_params = api_payload_to_create_params(payload.model_dump(exclude_unset=True, by_alias=True))
if payload.instance_type == "history":
if payload.history_id is None:
raise exceptions.RequestParameterInvalidException("Parameter history_id is required.")
@@ -395,7 +395,7 @@ class HistoriesService(ServiceBase, ConsumesModelStores, ServesExportStores):
duration=short_term_storage_target.duration,
user=trans.async_request_user,
export_association_id=export_association.id,
**payload.dict(),
**payload.model_dump(),
)
result = prepare_history_download.delay(request=request, task_user_id=getattr(trans.user, "id", None))
task_summary = async_task_summary(result)
@@ -412,7 +412,7 @@ class HistoriesService(ServiceBase, ConsumesModelStores, ServesExportStores):
user=trans.async_request_user,
history_id=history.id,
export_association_id=export_association.id,
**payload.dict(),
**payload.model_dump(),
)
result = write_history_to.delay(request=request, task_user_id=getattr(trans.user, "id", None))
task_summary = async_task_summary(result)
@@ -31,6 +31,7 @@ from galaxy.schema.schema import (
ShareWithStatus,
SharingOptions,
SharingStatus,
UserEmail,
UserIdentifier,
)
from galaxy.webapps.galaxy.services.notifications import NotificationService
@@ -108,7 +109,10 @@ class ShareableService:
users, errors = self._get_users(trans, payload.user_ids)
extra, users_to_notify = self._share_with_options(trans, item, users, errors, payload.share_option)
base_status = self._get_sharing_status(trans, item)
status = self.share_with_status_cls.model_construct(**base_status.model_dump(), extra=extra)
# Use dict() for a shallow field copy so nested UserEmail instances in
# users_shared_with survive; model_dump() would deep-serialize them to
# dicts that model_construct won't re-validate back to UserEmail.
status = self.share_with_status_cls.model_construct(**dict(base_status), extra=extra)
status.errors.extend(errors)
galaxy_url = str(trans.url_builder("/", qualified=True)).rstrip("/") if trans.url_builder else None
self._send_notification_to_users(users_to_notify, item, status, galaxy_url)
@@ -138,7 +142,7 @@ class ShareableService:
status = self.serializer.serialize_to_view(
item, user=trans.user, trans=trans, default_view="sharing", encode_id=False
)
status["users_shared_with"] = [{"id": a.user.id, "email": a.user.email} for a in item.users_shared_with]
status["users_shared_with"] = [UserEmail(id=a.user.id, email=a.user.email) for a in item.users_shared_with]
return SharingStatus(**status)
def _get_users(self, trans, emails_or_ids: list[UserIdentifier]) -> tuple[set[User], set[str]]:
+1 -1
View File
@@ -59,4 +59,4 @@ class ModelCache:
if not os.path.exists(parent_directory):
os.makedirs(parent_directory)
with open(cache_target, "w") as f:
json.dump(model_object.dict(), f)
json.dump(model_object.model_dump(), f)
+1 -1
View File
@@ -559,7 +559,7 @@ class StandaloneToolShedInstallationClient(ToolShedInstallationClient):
installed_repository.id,
)
response = CheckForUpdatesResponse(message=message, status=status)
response_dict = response.dict()
response_dict = response.model_dump()
if verify_no_updates:
assert "message" in response_dict
message = response_dict["message"]
+1
View File
@@ -4,6 +4,7 @@ asyncio_mode = auto
log_level = DEBUG
consider_namespace_packages = true
filterwarnings =
error::pydantic.warnings.PydanticDeprecatedSince20
ignore::DeprecationWarning:pkg_resources
ignore::DeprecationWarning:refgenconf
ignore::UserWarning:refgenconf