refactor(models): migrate service layer and API models to Pydantic v2#82
Merged
alexsander-souza merged 24 commits intocanonical:resolute_supportfrom Apr 7, 2026
Conversation
c90f82d to
2fc68bf
Compare
alemar99
requested changes
Apr 2, 2026
There was a problem hiding this comment.
Pull request overview
This PR migrates MAAS service-layer, API-server, and temporal worker Pydantic models from Pydantic v1 APIs to Pydantic v2 equivalents, updating validation/serialization patterns and adjusting FastAPI request/response model behavior accordingly.
Changes:
- Replaced Pydantic v1 APIs (
parse_obj,dict,copy,validator/root_validator,GenericModel,Config) with Pydantic v2 APIs (model_validate,model_dump,model_copy,field_validator/model_validator,BaseModel+Generic,ConfigDict). - Updated custom field types (e.g. MAC/network types) to use Pydantic v2 core schema hooks and
Annotatedvalidators. - Updated tests and request handlers/models to reflect new serialization/validation behavior and schema defaults.
Reviewed changes
Copilot reviewed 189 out of 189 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| src/tests/maasservicelayer/vault/api/test_apiclient.py | Update tests to model_validate. |
| src/tests/maasservicelayer/simplestreams/test_models.py | Replace .dict() with model_dump(). |
| src/tests/maasservicelayer/services/test_vlans.py | Replace .copy() with model_copy(). |
| src/tests/maasservicelayer/services/test_users.py | Replace .copy() with model_copy(). |
| src/tests/maasservicelayer/services/test_tags.py | Replace .copy() with model_copy(). |
| src/tests/maasservicelayer/services/test_subnets.py | Replace .copy() with model_copy(). |
| src/tests/maasservicelayer/services/test_notifications.py | Replace .copy() with model_copy(). |
| src/tests/maasservicelayer/services/test_image_sync.py | Replace .copy() with model_copy(). |
| src/tests/maasservicelayer/services/test_image_manifests.py | Replace .copy() with model_copy(). |
| src/tests/maasservicelayer/services/test_bootsourceselections.py | Replace .copy() with model_copy(). |
| src/tests/maasservicelayer/services/test_bootsourcecache.py | Adjust bootloader fixture shape. |
| src/tests/maasservicelayer/services/test_bootresourcesets.py | Add BootloaderFile fixture usage. |
| src/tests/maasservicelayer/services/test_bootresources.py | Ensure versions are strings. |
| src/tests/maasservicelayer/services/test_base.py | Remove v1-only required=False. |
| src/tests/maasservicelayer/models/test_configurations.py | Adjust proxy URL normalization expectation. |
| src/tests/maasservicelayer/db/repositories/test_subnet_utilization.py | Replace .dict() with model_dump(). |
| src/tests/maasservicelayer/db/repositories/test_machines.py | Replace .dict() with model_dump(). |
| src/tests/maasservicelayer/db/repositories/test_interfaces.py | Replace .dict() with model_dump(). |
| src/tests/maasservicelayer/db/repositories/test_base.py | Remove v1-only required=False. |
| src/tests/maasservicelayer/db/repositories/base.py | Use model_dump() for comparisons. |
| src/tests/maasservicelayer/auth/test_external_oauth.py | Replace .copy() with model_copy(). |
| src/tests/maasservicelayer/auth/macaroons/test_macaroon_client.py | Migrate to model_validate / v2 dumps. |
| src/tests/maasapiserver/v3/api/public/models/requests/test_base.py | Rename order-by mapping attribute. |
| src/tests/maasapiserver/v3/api/public/handlers/test_vlans.py | Replace .copy() with model_copy(). |
| src/tests/maasapiserver/v3/api/public/handlers/test_users.py | Replace .copy() with model_copy(). |
| src/tests/maasapiserver/v3/api/public/handlers/test_tags.py | Replace .copy() with model_copy(). |
| src/tests/maasapiserver/v3/api/public/handlers/test_racks.py | Replace .copy() with model_copy(). |
| src/tests/maasapiserver/v3/api/public/handlers/test_package_repositories.py | Replace .copy() with model_copy(). |
| src/tests/maasapiserver/v3/api/public/handlers/test_ipranges.py | Replace .copy() with model_copy(). |
| src/tests/maasapiserver/v3/api/public/handlers/test_boot_sources.py | Replace .copy() with model_copy(). |
| src/tests/maasapiserver/v3/api/public/handlers/test_boot_source_selections.py | Replace .copy() with model_copy(). |
| src/maastemporalworker/converter.py | Switch JSON encoding helper to to_jsonable_python. |
| src/maasservicelayer/vault/api/apiclient.py | Use model_dump_json and model_validate. |
| src/maasservicelayer/services/machines_v2.py | Replace Optional[...] with ` |
| src/maasservicelayer/services/image_sync.py | Replace .copy() with model_copy(). |
| src/maasservicelayer/models/vlans.py | Update optional typing to v2 style. |
| src/maasservicelayer/models/users.py | Update optional typing to v2 style. |
| src/maasservicelayer/models/usergroups.py | Update optional typing to v2 style. |
| src/maasservicelayer/models/ui_subnets.py | Update optional typing to v2 style. |
| src/maasservicelayer/models/tokens.py | Update optional typing to v2 style. |
| src/maasservicelayer/models/switches.py | Update optional typing to v2 style. |
| src/maasservicelayer/models/subnets.py | Update optional typing to v2 style. |
| src/maasservicelayer/models/staticipaddress.py | Update optional typing to v2 style. |
| src/maasservicelayer/models/sshkeys.py | Update optional typing to v2 style. |
| src/maasservicelayer/models/spaces.py | Update optional typing to v2 style. |
| src/maasservicelayer/models/reservedips.py | Update optional typing to v2 style. |
| src/maasservicelayer/models/notifications.py | Update optional typing to v2 style. |
| src/maasservicelayer/models/nodes.py | Update optional typing defaults. |
| src/maasservicelayer/models/neighbours.py | Add defaults for optional fields. |
| src/maasservicelayer/models/machines.py | Replace validators with field_validator. |
| src/maasservicelayer/models/ipranges.py | Update optional typing to v2 style. |
| src/maasservicelayer/models/interfaces.py | Update optional typing to v2 style. |
| src/maasservicelayer/models/filestorage.py | Update optional typing to v2 style. |
| src/maasservicelayer/models/fields.py | Rework network/MAC types for v2. |
| src/maasservicelayer/models/fabrics.py | Update optional typing to v2 style. |
| src/maasservicelayer/models/events.py | Update optional typing to v2 style. |
| src/maasservicelayer/models/domains.py | Update optional typing to v2 style. |
| src/maasservicelayer/models/dnsresources.py | Update optional typing to v2 style. |
| src/maasservicelayer/models/dnsresourcerecordsets.py | Fix optional typing syntax. |
| src/maasservicelayer/models/dnsdata.py | Update optional typing to v2 style. |
| src/maasservicelayer/models/dhcpsnippets.py | Update optional typing to v2 style. |
| src/maasservicelayer/models/consumers.py | Update optional typing to v2 style. |
| src/maasservicelayer/models/bootsources.py | Update optional typing to v2 style. |
| src/maasservicelayer/models/base.py | Switch to ConfigDict + v2 dumps. |
| src/maasservicelayer/models/auth.py | Update optional typing + field defaults. |
| src/maasservicelayer/models/agents.py | Update optional typing formatting. |
| src/maasservicelayer/db/init.py | Switch JSON serializer to to_jsonable_python. |
| src/maasservicelayer/builders/zones.py | Builder typing to v2 unions. |
| src/maasservicelayer/builders/vmcluster.py | Builder typing to v2 unions. |
| src/maasservicelayer/builders/vlans.py | Builder typing to v2 unions. |
| src/maasservicelayer/builders/users.py | Builder typing to v2 unions. |
| src/maasservicelayer/builders/usergroups.py | Builder typing to v2 unions. |
| src/maasservicelayer/builders/tokens.py | Builder typing to v2 unions. |
| src/maasservicelayer/builders/tags.py | Builder typing to v2 unions. |
| src/maasservicelayer/builders/switches.py | Builder typing to v2 unions. |
| src/maasservicelayer/builders/subnets.py | Builder CIDR typing changes. |
| src/maasservicelayer/builders/staticroutes.py | Builder typing to v2 unions. |
| src/maasservicelayer/builders/staticipaddress.py | Builder typing to v2 unions. |
| src/maasservicelayer/builders/sslkeys.py | Builder typing to v2 unions. |
| src/maasservicelayer/builders/sshkeys.py | Builder typing to v2 unions. |
| src/maasservicelayer/builders/spaces.py | Builder typing to v2 unions. |
| src/maasservicelayer/builders/service_status.py | Builder typing to v2 unions. |
| src/maasservicelayer/builders/scriptresult.py | Builder typing to v2 unions. |
| src/maasservicelayer/builders/resource_pools.py | Builder typing to v2 unions. |
| src/maasservicelayer/builders/reservedips.py | Builder typing to v2 unions. |
| src/maasservicelayer/builders/rdns.py | Builder typing to v2 unions. |
| src/maasservicelayer/builders/racks.py | Builder typing to v2 unions. |
| src/maasservicelayer/builders/package_repositories.py | Builder typing to v2 unions. |
| src/maasservicelayer/builders/openfga_tuple.py | Builder typing to v2 unions. |
| src/maasservicelayer/builders/notifications.py | Builder typing to v2 unions. |
| src/maasservicelayer/builders/nodes.py | Builder typing to v2 unions. |
| src/maasservicelayer/builders/nodegrouptorackcontrollers.py | Builder typing to v2 unions. |
| src/maasservicelayer/builders/neighbours.py | Builder typing to v2 unions. |
| src/maasservicelayer/builders/mdns.py | Builder typing to v2 unions. |
| src/maasservicelayer/builders/machines.py | Builder typing to v2 unions. |
| src/maasservicelayer/builders/legacybootsourceselections.py | Builder typing to v2 unions. |
| src/maasservicelayer/builders/leases.py | Builder typing to v2 unions. |
| src/maasservicelayer/builders/ipranges.py | Builder typing to v2 unions. |
| src/maasservicelayer/builders/interfaces.py | Builder typing to v2 unions. |
| src/maasservicelayer/builders/image_manifests.py | Builder generic union refactor. |
| src/maasservicelayer/builders/forwarddnsserver.py | Builder typing to v2 unions. |
| src/maasservicelayer/builders/filestorage.py | Builder typing to v2 unions. |
| src/maasservicelayer/builders/fabrics.py | Builder typing to v2 unions. |
| src/maasservicelayer/builders/external_auth.py | Builder typing to v2 unions. |
| src/maasservicelayer/builders/events.py | Builder typing to v2 unions. |
| src/maasservicelayer/builders/domains.py | Builder typing to v2 unions. |
| src/maasservicelayer/builders/dnsresources.py | Builder typing to v2 unions. |
| src/maasservicelayer/builders/dnspublications.py | Builder typing to v2 unions. |
| src/maasservicelayer/builders/dnsdata.py | Builder typing to v2 unions. |
| src/maasservicelayer/builders/django_session.py | Builder typing to v2 unions. |
| src/maasservicelayer/builders/dhcpsnippets.py | Builder typing to v2 unions. |
| src/maasservicelayer/builders/consumers.py | Builder typing to v2 unions. |
| src/maasservicelayer/builders/configurations.py | Builder typing to v2 unions. |
| src/maasservicelayer/builders/bootstraptokens.py | Builder typing to v2 unions. |
| src/maasservicelayer/builders/bootsourceselections.py | Builder typing to v2 unions. |
| src/maasservicelayer/builders/bootsources.py | Builder typing to v2 unions. |
| src/maasservicelayer/builders/bootsourcecache.py | Builder typing to v2 unions. |
| src/maasservicelayer/builders/bootresourcesets.py | Builder typing to v2 unions. |
| src/maasservicelayer/builders/bootresources.py | Builder typing to v2 unions. |
| src/maasservicelayer/builders/bootresourcefilesync.py | Builder typing to v2 unions. |
| src/maasservicelayer/builders/bootresourcefiles.py | Builder typing to v2 unions. |
| src/maasservicelayer/builders/bmc.py | Builder typing to v2 unions. |
| src/maasservicelayer/builders/agents.py | Builder typing to v2 unions. |
| src/maasservicelayer/auth/macaroons/models/responses.py | v2 validators + model validators. |
| src/maasservicelayer/auth/macaroons/models/requests.py | v2 typing + aliases. |
| src/maasservicelayer/auth/macaroons/macaroon_client.py | Switch to model_validate / v2 dumps. |
| src/maasapiserver/v3/middlewares/auth.py | Use model_dump() for response composition. |
| src/maasapiserver/v3/api/public/models/responses/zones.py | Make kind a Pydantic field. |
| src/maasapiserver/v3/api/public/models/responses/vlans.py | Make kind a Pydantic field. |
| src/maasapiserver/v3/api/public/models/responses/users.py | Make kind a Pydantic field. |
| src/maasapiserver/v3/api/public/models/responses/usergroups.py | Make kind a Pydantic field. |
| src/maasapiserver/v3/api/public/models/responses/usergroup_members.py | Make kind a Pydantic field. |
| src/maasapiserver/v3/api/public/models/responses/ui_subnets.py | Make kind a Pydantic field. |
| src/maasapiserver/v3/api/public/models/responses/tags.py | Make kind a Pydantic field. |
| src/maasapiserver/v3/api/public/models/responses/subnets.py | Make kind a Pydantic field. |
| src/maasapiserver/v3/api/public/models/responses/staticroutes.py | Make kind a Pydantic field. |
| src/maasapiserver/v3/api/public/models/responses/sslkey.py | Make kind a Pydantic field. |
| src/maasapiserver/v3/api/public/models/responses/sshkeys.py | Make kind a Pydantic field. |
| src/maasapiserver/v3/api/public/models/responses/spaces.py | Make kind a Pydantic field. |
| src/maasapiserver/v3/api/public/models/responses/resource_pools.py | Make kind a Pydantic field. |
| src/maasapiserver/v3/api/public/models/responses/reservedips.py | Make kind a Pydantic field. |
| src/maasapiserver/v3/api/public/models/responses/racks.py | Make kind a Pydantic field. |
| src/maasapiserver/v3/api/public/models/responses/package_repositories.py | Make kind a Pydantic field. |
| src/maasapiserver/v3/api/public/models/responses/oauth2.py | Make kind a Pydantic field. |
| src/maasapiserver/v3/api/public/models/responses/notifications.py | Make kind a Pydantic field. |
| src/maasapiserver/v3/api/public/models/responses/machines.py | Make kind a Pydantic field. |
| src/maasapiserver/v3/api/public/models/responses/ipranges.py | Make kind a Pydantic field. |
| src/maasapiserver/v3/api/public/models/responses/interfaces.py | Use ConfigDict; make kind a field. |
| src/maasapiserver/v3/api/public/models/responses/files.py | Make kind a Pydantic field. |
| src/maasapiserver/v3/api/public/models/responses/fabrics.py | Make kind a Pydantic field. |
| src/maasapiserver/v3/api/public/models/responses/events.py | Make kind a Pydantic field. |
| src/maasapiserver/v3/api/public/models/responses/entitlements.py | Make kind a Pydantic field. |
| src/maasapiserver/v3/api/public/models/responses/domains.py | Make kind a Pydantic field. |
| src/maasapiserver/v3/api/public/models/responses/discoveries.py | Make kind a Pydantic field. |
| src/maasapiserver/v3/api/public/models/responses/configurations.py | Make kind a Pydantic field. |
| src/maasapiserver/v3/api/public/models/responses/boot_sources.py | Make kind a Pydantic field. |
| src/maasapiserver/v3/api/public/models/responses/boot_source_selections.py | Make kind a Pydantic field. |
| src/maasapiserver/v3/api/public/models/responses/boot_resources.py | Make kind a Pydantic field. |
| src/maasapiserver/v3/api/public/models/responses/boot_images_common.py | Make kind a Pydantic field. |
| src/maasapiserver/v3/api/public/models/responses/base.py | Replace GenericModel with v2 generics + ConfigDict. |
| src/maasapiserver/v3/api/public/models/responses/agents.py | Make kind a Pydantic field. |
| src/maasapiserver/v3/api/public/models/requests/zones.py | Replace validator with field_validator. |
| src/maasapiserver/v3/api/public/models/requests/users.py | Replace validator with field_validator. |
| src/maasapiserver/v3/api/public/models/requests/ui_subnets.py | Rename order-by mapping attribute. |
| src/maasapiserver/v3/api/public/models/requests/tags.py | Replace validator with field_validator. |
| src/maasapiserver/v3/api/public/models/requests/subnets.py | Rewrite gateway validator for v2. |
| src/maasapiserver/v3/api/public/models/requests/staticroutes.py | Replace conint with Annotated. |
| src/maasapiserver/v3/api/public/models/requests/sslkeys.py | Replace validator with field_validator. |
| src/maasapiserver/v3/api/public/models/requests/package_repositories.py | Replace root_validator with model_validator. |
| src/maasapiserver/v3/api/public/models/requests/notifications.py | Replace root_validator with model_validator. |
| src/maasapiserver/v3/api/public/models/requests/ipranges.py | Add model-level validation in v2 style. |
| src/maasapiserver/v3/api/public/models/requests/fabrics.py | Replace validator with field_validator. |
| src/maasapiserver/v3/api/public/models/requests/external_auth.py | Replace v1 validator signature with ValidationInfo. |
| src/maasapiserver/v3/api/public/models/requests/domains.py | Replace root_validator with model_validator. |
| src/maasapiserver/v3/api/public/models/requests/discoveries.py | Replace root_validator with model_validator. |
| src/maasapiserver/v3/api/public/models/requests/configurations.py | Update type error message extraction. |
| src/maasapiserver/v3/api/public/models/requests/boot_sources.py | Replace v1 validators; use ConfigDict(extra=\"forbid\"). |
| src/maasapiserver/v3/api/public/models/requests/boot_source_selections.py | Replace unique_items with custom validator. |
| src/maasapiserver/v3/api/public/models/requests/base.py | Update order-by mapping, validators, query params. |
| src/maasapiserver/v3/api/public/models/dnsresourcerecordsets.py | Replace validators with field_validator. |
| src/maasapiserver/v3/api/public/handlers/boot_source_selections.py | Replace conlist with Annotated + Field constraints. |
| src/maasapiserver/v3/api/public/handlers/boot_resources.py | Replace conlist; add explicit header dependency. |
| src/maasapiserver/v3/api/internal/models/responses/agent.py | Make kind a Pydantic field; optional typing updates. |
| src/maasapiserver/v3/api/internal/models/requests/agent.py | Replace validator with field_validator. |
| src/maasapiserver/main.py | Wrap validation exception handler for new signature. |
| src/maasapiserver/common/api/models/responses/errors.py | Make kind a Pydantic field. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
db00a11 to
f4a87b3
Compare
alemar99
requested changes
Apr 3, 2026
Replace all Pydantic v1 patterns with their v2 equivalents across the 35 service layer domain models, auth models, and simplestreams models. - GenericModel → BaseModel (generics built-in to BaseModel in v2) - class Config → model_config = ConfigDict(...) - .dict() → .model_dump(); .parse_obj() → .model_validate() - @validator → @field_validator with @classmethod - @root_validator → @model_validator(mode="before"/"after") - Optional[X] → X | None (PEP 604) - configurations.py: Config(GenericModel, Generic[T]) → Config(BaseModel, Generic[T]) with ClassVar[ConfigDict]; underscore- prefixed class-level regex patterns annotated as ClassVar to prevent Pydantic v2 misidentifying them as PrivateAttr - simplestreams/models.py: switch alias → validation_alias; remove dict() overrides; add populate_by_name=True; add squashfs = None default; add @model_validator(mode="after") to enforce squashfs-or-root_image_gz presence invariant - fields.py: migrate custom type validators from __get_validators__ / __modify_schema__ to __get_pydantic_core_schema__ (v2 API)
Rewrite the builder code generator to emit Pydantic v2-compatible
annotations and regenerate all 56 builder files.
- generate_builders.py: replace ModelField with FieldDef dataclass;
use model_fields instead of __fields__; use native union syntax
(annotation | Unset) instead of mutating ModelField objects; emit
Field(default=UNSET) without the invalid v2 required=False parameter
- 56 builder files: remove Field(required=False) which is not a valid
Pydantic v2 Field parameter; verify reproducibility with:
python utilities/generate_builders.py && git diff --exit-code
Migrate all maasapiserver v3 request and response models to Pydantic v2 and fix FastAPI integration points that changed with the v2 upgrade. - responses/base.py: GenericModel -> BaseModel (generics built-in); class Config -> model_config = ConfigDict(populate_by_name=True) - Response kind fields: bare class attribute -> str = Field(default=...) across all 33 public response files, 4 internal response models, and ErrorBodyResponse to restore JSON serialization (Pydantic v2 rejects non-annotated class attributes entirely) - Request models: @validator -> @field_validator with @classmethod; @root_validator -> @model_validator(mode="after") with instance method signature; extra=Extra.forbid -> ConfigDict(extra="forbid"); conlist -> Annotated[list[int], Field(min_length=1)] - boot_source_selections.py: restore unique-items constraint via @model_validator(mode="after") checking duplicate tuples - boot_resources.py handler: replace Header model injection with explicit get_boot_resource_create_request() dependency function (Pydantic v2 + FastAPI no longer support direct Header model injection via Depends()) - auth.py: .dict() -> .model_dump() - db/__init__.py, converter.py: pydantic_encoder (removed in v2) -> pydantic_core.to_jsonable_python
Bring in the updated generate_builders.py from sandbox and regenerate
all 56 builder files to match.
Generator improvements:
- Use AST-based method/import extraction to avoid import-time failures
when builders reference fields not yet present in the environment
- Emit X | Unset pipe-union syntax instead of Union[X, Unset]
- Add normalize_imports() to consolidate same-module imports
- Add outputs_match() using AST dump comparison for robust change
detection (whitespace-insensitive)
- Add EXCLUDED_METHODS set covering magic methods, Pydantic internals,
and Python 3.14 __annotate__/__annotate_func__
- Preserve file header (copyright comment) from existing builder files
- Handle import failures gracefully throughout BuilderCollection
Builder files: regenerated to use X | Unset syntax; drop the now-
redundant 'from typing import Union' import. subnets.py also corrected
to use IPv4v6Network instead of the stale IPv4Network | IPv6Network.
Verify reproducibility with:
python utilities/generate_builders.py --check
Bring in updated models from sandbox. - Use ABCMeta metaclass instead of ABC mixin for Pydantic compatibility - Make Product and SimpleStreamsProductList generic (Generic[VersionT/ProductT]) so get_latest_version/get_version_by_name return concrete types - Switch from populate_by_name=True to validate_by_alias=True + serialize_by_alias=True - Replace validation_alias with alias on hyphenated fields (consistent alias used for both input and output) - Fix get_version_by_name: was comparing v.name, now correctly uses v.version_name - Add at_least_one_file_present validator to BootloaderVersion - Fix ImageFile.kpackage: add missing default=None - Fix support_eol on ImageProduct: remove default so missing value is caught - Guard preprocess_versions/preprocess_products against non-dict input - Rename private _validate_* methods to validate_* (no leading underscore needed) - Add type annotations to all validator signatures - Replace Union[...] with | union syntax for SimpleStreamsProductListType - Drop unused List, Type, Union imports; add Generic, TypeVar
…t layer Complete the remaining Pydantic v1 → v2 migration items not covered by the earlier model/builder/API commits. - .json() → .model_dump_json(): macaroon_client, vault apiclient, test_macaroon_client - .copy() → .model_copy(): image_sync service, external_oauth tests, bootsource* tests, image_* tests, notifications/subnets/tags/users/ vlans service tests - .dict() → .model_dump(): repository base tests, test_interfaces, test_machines, test_subnet_utilization, simplestreams test_models - machines_v2: migrate 25 Optional[X] model fields to X | None = None (v2 no longer infers None default from Optional) - test_bootresources: wrap randint version values in str() to satisfy strict field typing in Pydantic v2 - test_bootresourcesets/test_bootsourcecache: replace bare None bootloader file fields with populated BootloaderFile fixtures
…uality - Add missing newlines at EOF for fields.py and base.py to prevent lint warnings - Rename _order_by_columns to order_by_columns with explanation: leading underscores conflict with Pydantic v2 ClassVar handling; add comment for maintainers - Move url_preserve_empty_path=True from base Config class to HttpProxyConfig only to avoid applying URL preservation logic to non-URL configuration types - Add explanatory comments for FreeTextSearchQueryParam Header/Query field requirement - Replace deprecated conint() with Annotated[int, Field(...)] in staticroutes.py - Update all handler test files to use model_copy() instead of deprecated copy() for Pydantic v2 compatibility (boot_sources, vlans, ipranges, package_repositories, boot_source_selections, racks, tags, users tests) - Add core_schema documentation to MacAddress and PackageRepoUrl explaining why __get_pydantic_core_schema__ is required for str subclasses with custom __new__ - Remove boolean flag duplication in BuilderModule._generate_output() - Add debug logging to generate_builders.py for AST extraction and import failures to improve visibility when builders fall back to AST-only mode Fixes: Missing newlines, overly broad config settings, inconsistent v1->v2 migrations in test code, and improves debugging for builder generation edge cases.
- errors.py: HTTP_422_UNPROCESSABLE_ENTITY → HTTP_422_UNPROCESSABLE_CONTENT - boot_resources/boot_source_selections: restore no-duplicate-ids constraint on bulk-delete query params (conlist unique_items equivalent via AfterValidator) - boot_resources.py request model: Header → Field annotations; explicit default=None on optional fields - boot_images_common.py: explicit default=None on optional response fields - base.py: fix ClassVar OrderByQueryFilter default to avoid Pydantic v2 error - app.py: wrap AsyncClient in ASGITransport (AsyncClient(app=...) deprecated in httpx 0.28) - test_configurations.py: fix overly strict URL trailing-slash assertion
model_dump_json() returns a JSON string, not a dict; passing it as the json= argument to aiohttp double-encodes the payload. model_dump(mode="json") returns a JSON-serialisable dict as expected.
- Replace Pydantic v1 .json() with v2 .model_dump_json() - Update subnet CIDR overlap test to use proper parameter compilation - Fix test setup method naming convention (setup_method) - Remove redundant setup() calls in async test methods - Update JSON formatting in vault API client assertions Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Fix implicit string concatenation in test parametrization - Add leeway to JWT claims validation for clock skew tolerance - Allow blank subject claim in JWT validation All tests now pass.
…derModule The outputs_match() method was calling self.get_output() but BuilderModule only had _generate_output(). Rename to get_output() for consistency with BuilderModel and to match the expected API.
- Update AsyncClient fixture to use ASGITransport pattern in test_response_finalizer.py - Fix ConfigurationResponse.value field to be optional (Any | None = None) - Migrate test_auth.py from .dict() to .model_dump() for Pydantic v2 - Fix metadata comparison in test_get_active_oauth_provider_success using model_dump() - Update validation error expectations in test_notifications.py for Pydantic v2 format - Update validation error messages in test_sshkeys.py to match Pydantic v2 error format
… type Add UniqueList[T] to maasservicelayer/models/fields.py using pydantic v2's AfterValidator pattern. Raises a typed 'unique_list' PydanticCustomError on duplicates and emits uniqueItems: true in the JSON/OpenAPI schema via Field(json_schema_extra). Replace _no_duplicate_ids + AfterValidator in bulk_delete_custom_images and the manual model_validator loop in BulkSelectionRequest with UniqueList. SelectionRequest gains frozen=True and an explicit __hash__ so it satisfies the Hashable bound required by UniqueList.
- Fix subnets.py: Use is_link_local property instead of incorrect fe80::/64 check - Fix subnet.py: Update comment to reference correct fe80::/10 mask - Fix test_subnet.py: Update test data to use fe80::/10 instead of fe80::/64 The IPv6 link-local range is fe80::/10 (not /64). The is_link_local property correctly checks the full range per RFC 4291.
- Store iat and exp claims as integer timestamps (second precision) joserfc validates NumericDate by comparing against int(time.time()), so float timestamps would always appear 'in the future'. - Fix test assertion: token.issued can be up to ~1s behind now due to integer truncation, so use relative time check instead of >=. - Replace invalid empty-subject test case with valid one. Empty 'sub' claim is correctly rejected by joserfc when essential: True.
- Change builder_model fixture to return type[OAuthProviderBuilder] (class type) - Update test method type hints to properly indicate builder_model parameter as class type - Instantiate builder with no arguments and only set required properties per test - Fixes tests: test_create_conflict, test_update_provider_success, test_update_provider_enables_when_none_enabled, test_update_provider_conflict - All 50 TestExternalOAuthService tests pass
Replace all Pydantic v1 patterns with their v2 equivalents across the 35 service layer domain models and auth models. - GenericModel → BaseModel (generics built-in to BaseModel in v2) - class Config → model_config = ConfigDict(...) - .dict() → .model_dump(); .parse_obj() → .model_validate() - @validator → @field_validator with @classmethod - Optional[X] → X | None (PEP 604) - configurations.py: Config(GenericModel, Generic[T]) → Config(BaseModel, Generic[T]) with ClassVar[ConfigDict] - simplestreams/models.py: add squashfs = None default; add @model_validator(mode="after") to enforce squashfs-or-root_image_gz presence invariant (replaces implicit v1 field constraint behaviour)
b6c6341 to
f77ee17
Compare
7d50283
into
canonical:resolute_support
1 of 2 checks passed
alexsander-souza
added a commit
that referenced
this pull request
Apr 8, 2026
…#82) Replace all Pydantic v1 APIs with their v2 equivalents across maasservicelayer, maasapiserver, and maastemporalworker. Key changes include replacing GenericModel with BaseModel+Generic, validator with field_validator/model_validator, dict()/parse_obj() with model_dump()/model_validate(), and Config inner class with ConfigDict. Modernise the builder code generator (generate_builders.py) to use Pydantic v2 model_fields/FieldInfo API and AST-based parsing for import preservation.
alexsander-souza
added a commit
that referenced
this pull request
Apr 8, 2026
…#82) Replace all Pydantic v1 APIs with their v2 equivalents across maasservicelayer, maasapiserver, and maastemporalworker. Key changes include replacing GenericModel with BaseModel+Generic, validator with field_validator/model_validator, dict()/parse_obj() with model_dump()/model_validate(), and Config inner class with ConfigDict. Modernise the builder code generator (generate_builders.py) to use Pydantic v2 model_fields/FieldInfo API and AST-based parsing for import preservation.
alexsander-souza
added a commit
that referenced
this pull request
Apr 9, 2026
…#82) Replace all Pydantic v1 APIs with their v2 equivalents across maasservicelayer, maasapiserver, and maastemporalworker. Key changes include replacing GenericModel with BaseModel+Generic, validator with field_validator/model_validator, dict()/parse_obj() with model_dump()/model_validate(), and Config inner class with ConfigDict. Modernise the builder code generator (generate_builders.py) to use Pydantic v2 model_fields/FieldInfo API and AST-based parsing for import preservation.
alexsander-souza
added a commit
that referenced
this pull request
Apr 17, 2026
…#82) Replace all Pydantic v1 APIs with their v2 equivalents across maasservicelayer, maasapiserver, and maastemporalworker. Key changes include replacing GenericModel with BaseModel+Generic, validator with field_validator/model_validator, dict()/parse_obj() with model_dump()/model_validate(), and Config inner class with ConfigDict. Modernise the builder code generator (generate_builders.py) to use Pydantic v2 model_fields/FieldInfo API and AST-based parsing for import preservation.
alexsander-souza
added a commit
that referenced
this pull request
Apr 17, 2026
…#82) Replace all Pydantic v1 APIs with their v2 equivalents across maasservicelayer, maasapiserver, and maastemporalworker. Key changes include replacing GenericModel with BaseModel+Generic, validator with field_validator/model_validator, dict()/parse_obj() with model_dump()/model_validate(), and Config inner class with ConfigDict. Modernise the builder code generator (generate_builders.py) to use Pydantic v2 model_fields/FieldInfo API and AST-based parsing for import preservation.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Replace all Pydantic v1 APIs with their v2 equivalents across
maasservicelayer, maasapiserver, and maastemporalworker. Key changes
include replacing GenericModel with BaseModel+Generic, validator with
field_validator/model_validator, dict()/parse_obj() with
model_dump()/model_validate(), and Config inner class with ConfigDict.
Modernise the builder code generator (generate_builders.py) to use
Pydantic v2 model_fields/FieldInfo API and AST-based parsing for import
preservation.