Skip to content

EnvironmentConfig.handle_deprecated_fields validator triggers its own DeprecationWarning on every construction #1693

Description

@NicoDeVeaux

Summary

EnvironmentConfig emits a DeprecationWarning for the memory and storage fields every time it is constructed, even when the caller uses the new memory_mb / storage_mb fields exclusively. The warning originates from Harbor's own handle_deprecated_fields validator, which accesses self.memory and self.storage to check for None — and Pydantic 2 emits a DeprecationWarning on attribute access for any field declared with Field(deprecated=...).

Net effect: any project that runs its test suite under python -W error::DeprecationWarning fails immediately on a clean Harbor 0.7.1 install, even with no use of deprecated fields anywhere in the code or task tomls.

Versions

  • Harbor: 0.7.1
  • Pydantic: 2.12.5
  • Python: 3.14.3

Minimal repro

import warnings
warnings.simplefilter("error", DeprecationWarning)

from harbor.models.task.config import EnvironmentConfig
EnvironmentConfig(docker_image="alpine")

Result:

DeprecationWarning: Use 'memory_mb' instead. This field will be removed in a future version.
  File ".../harbor/models/task/config.py", line 202, in handle_deprecated_fields
    if self.memory is not None:

No deprecated field was set by the caller. The warning fires purely because the validator reads self.memory.

Root cause

harbor/models/task/config.py:

# line 165
memory: str | None = Field(
    default=None,
    deprecated="Use 'memory_mb' instead. This field will be removed in a future version.",
    exclude=True,
)
storage: str | None = Field(
    default=None,
    deprecated="Use 'storage_mb' instead. This field will be removed in a future version.",
    exclude=True,
)

# line 200
@model_validator(mode="after")
def handle_deprecated_fields(self) -> "EnvironmentConfig":
    if self.memory is not None:   # ← Pydantic raises DeprecationWarning here
        ...
    if self.storage is not None:  # ← and here
        ...
    return self

Pydantic 2 wires Field(deprecated=...) such that any attribute read goes through a property that emits the warning. The if self.memory is not None: check inside the validator is itself an attribute access and therefore triggers it — regardless of whether the caller ever provided memory.

Expected behavior

Constructing EnvironmentConfig with only the supported fields (e.g. docker_image="alpine" or memory_mb=512) should not emit any DeprecationWarning. The warning should fire only when the caller actually provided memory= or storage=.

Proposed fix

Read the attribute via __dict__ (or self.model_dump(include={"memory", "storage"})) to bypass the Pydantic deprecation property, or check self.model_fields_set first:

@model_validator(mode="after")
def handle_deprecated_fields(self) -> "EnvironmentConfig":
    # Bypass Field(deprecated=...) attribute property to avoid emitting
    # our own DeprecationWarning when the caller didn't set the field.
    memory = self.__dict__.get("memory")
    if memory is not None:
        warnings.warn(
            "The 'memory' field is deprecated. Use 'memory_mb' instead.",
            DeprecationWarning,
            stacklevel=2,
        )
        self.memory_mb = self._parse_size_to_mb(memory)
        self.__dict__["memory"] = None

    storage = self.__dict__.get("storage")
    if storage is not None:
        warnings.warn(
            "The 'storage' field is deprecated. Use 'storage_mb' instead.",
            DeprecationWarning,
            stacklevel=2,
        )
        self.storage_mb = self._parse_size_to_mb(storage)
        self.__dict__["storage"] = None

    return self

Equivalent alternative using model_fields_set:

if "memory" in self.model_fields_set:
    memory = self.__dict__.get("memory")
    ...

Impact

Downstream projects that run -W error::DeprecationWarning for regression hygiene (catching real deprecations before they bite during dependency upgrades) cannot do so against Harbor 0.7.1 without filtering this specific warning. The workaround is fine short-term but adds noise to project configs.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions