如何:创建类型安全的跨 pydantic 版本兼容的配置混合

问题描述 投票:0回答:1

描述

我们已开始在分布式 MLops 平台中添加对 pydantic v2 的支持。我们目前面临的一个核心挑战是,pydantic 实际上是我们平台中每个 python 包的一部分,并用于各种用例:

  • 运行 fastapi + pydantic 的微服务
  • 培训+处理作业的配置管理
  • 建模结构化日志有效负载
  • 还有更多

我们的核心问题是,我们需要在未知的时间内支持这两个版本。这主要是因为有些 ML 模型已经使用 pydantic v1 进行了训练,需要使用 v1 进行服务,而可能存在使用 v2 进行训练的新 ML 模型,也需要使用 v2 进行服务。在运行时,尤其是在我们的 ML 端点中,我们可能安装了 v1 或 v2,并且我们需要能够支持这两者。

这使我们陷入这样的境地:我们平台的某些组件需要在未知的时间内(可能是几周、几个月……)与两个版本兼容。这就需要提供一个跨版本兼容层,使转换更容易。

虽然我们已经能够获得最常用的 pydantic 功能测试和类型安全交叉版本(使用某种包装器/适配器代码),但 pydantic 模型配置仍然困扰着我们。这是因为可能有成百上千的地方,基本模型以不同的配置设置(可变或不可变、有或没有别名、有额外或没有等)保存在代码中。

为了缓解这种过渡,我们考虑创建跨版本兼容的 mixin 类,可以请求这些类来创建配置的 pydantic 模型。这有点像这个想法:

# type checking this code does work via something like `python -m mypy foo.py --always-true PYDANTIC_V1`
import packaging.version
import pydantic

PYDANTIC_V1 = packaging.version.parse(pydantic.__version__).major == 1

if PYDANTIC_V1:
    class Immutable(pydantic.BaseModel):
        """mixin to make a pydantic model immutable"""

        class Config:
            allow_mutation = False
else:
    from pydantic import ConfigDict


    class Immutable(pydantic.BaseModel):
        """mixin to make a pydantic model immutable"""

        model_config = ConfigDict(frozen=True)

为了使模型不可变,我们的客户可以简单地以这种方式声明他们的模型:

from foo import Immutable

class JobConfiguration(Immutable):
    foo: str

但是,对于多重继承,这会变得很棘手。我们在 v1 中尝试了两种可能的方法,但都无法通过 pydantic mypy 插件进行类型检查

方法 1(使用 Config 类)

from typing import Any

from pydantic import BaseModel, Field


class Immutable(BaseModel):
    """mixin to make a pydantic model immutable"""

    class Config:
        allow_mutation = False


class WithAliases(BaseModel):
    """mixin to make a pydantic model to accept and serialize by alias"""

    class Config:
        allow_population_by_field_name = True

    def json(self, by_alias: bool = True, **kwargs: Any) -> str:
        return super().json(by_alias=by_alias, **kwargs)


class Customer(Immutable, WithAliases):
    id_: str = Field(..., alias="id")


instance = Customer(id_="foo")
instance.id_ = "1"

在此代码上运行 mypy 失败

python -m mypy v1.py --always-true PYDANTIC_V1 
v1.py:23: error: Definition of "Config" in base class "Immutable" is incompatible with definition in base class "WithAliases"  [misc]
v1.py:28: error: Property "id_" defined in "Customer" is read-only  [misc]
Found 2 errors in 1 file (checked 1 source file)

方法 2(使用类 kwargs)

from typing import Any

from pydantic import BaseModel, Field


class Immutable(BaseModel, allow_mutation=False):
    """mixin to make a pydantic model immutable"""


class WithAliases(BaseModel, allow_population_by_field_name=True):
    """mixin to make a pydantic model to accept and serialize by alias"""

    def json(self, by_alias: bool = True, **kwargs: Any) -> str:
        return super().json(by_alias=by_alias, **kwargs)


class Customer(Immutable, WithAliases):
    id_: str = Field(..., alias="id")


instance = Customer(id_="foo")
instance.id_ = "1"

在此代码上运行 mypy 也失败,尽管有不同的错误消息

python -m mypy v2.py --always-true PYDANTIC_V1
v2.py:21: error: Unexpected keyword argument "id_" for "Customer"; did you mean "id"?  [call-arg]
Found 1 error in 1 file (checked 1 source file)

有谁知道在 pydantic v1 中使用多个配置混合类并让它们通过 mypy 类型检查的方法吗?

系统详情

Virtualenv
Python:         3.10.14
Implementation: CPython

System
Platform:   darwin
OS:         posix

Dependencies
mypy              1.9.0
mypy-extensions   1.0.0
packaging         24.0
pydantic          1.10.10
tomli             2.0.1
typing-extensions 4.11.0

mypy 配置

[tool.mypy]
plugins = [
  "pydantic.mypy"
]
strict = true
implicit_reexport = true
ignore_missing_imports = true
check_untyped_defs = true
show_error_codes = true
warn_redundant_casts = true
warn_unused_configs = true
warn_unused_ignores = true
# for strict mypy: (this is the tricky one :-))
disallow_untyped_defs = true

[tool.pydantic-mypy]
init_forbid_extra = true
init_typed = true
warn_required_dynamic_aliases = true
warn_untyped_fields = true
python mypy pydantic typing
1个回答
0
投票

要处理 Pydantic v1 和 v2,请考虑为共享设置创建抽象基配置类,同时利用基于运行时检测到的 Pydantic 版本的条件导入。这样,您可以根据安装的版本动态调整使用哪些 Pydantic 特性或功能。使用环境变量或配置文件来指定系统不同部分所需的 Pydantic 版本。这种方法可以实现灵活性并保持跨平台的兼容性,而无需过早地提交单个版本。此外,彻底的测试对于确保跨版本的兼容性和功能至关重要。

© www.soinside.com 2019 - 2024. All rights reserved.