簡體   English   中英

正確向后兼容的方式遷移到現代 attrs/cattrs 樣式?

[英]Correct backwards-compatible way to migrate to modern attrs/cattrs style?

問題

我有一組項目,我從 2020 年初開始使用 attrs v19.3.0 和 cattrs 進行序列化/反序列化。 將這些項目中的類從舊的@attr.s / attr.ib樣式遷移到帶有@define@frozen的現代樣式的正確完全向后兼容的方法是什么?

問題說明

我的假設是我可以混合和匹配新舊樣式類,並且總有一種方法可以使用新注釋獲得等效功能。 我一直在一次轉換一個類,在每次轉換后檢查單元測試失敗和 MyPy 警告等。

我現在已經轉移到這段代碼中最復雜的對象層次結構,它也使用 cattrs 進行序列化/反序列化。 我找不到向后兼容的解決方案。 只要我轉換了模塊中的一個類,我的測試套件就會立即失敗,並出現與 cattr 相關的錯誤。

最小測試用例

我最初在這里展示了一些實際的代碼,但這太混亂了,沒有用。 我現在已將問題縮小到一個小測試用例。

這是帶有@attr.sattr.ib的舊式類。 使用此代碼,測試用例通過。

from __future__ import annotations

from typing import Dict
import attr
import cattrs

converter = cattrs.Converter()

@attr.s
class Game:
    players = attr.ib(type=Dict[str, str])
    def copy(self) -> Game:
        return converter.structure(converter.unstructure(self), Game)

class TestGame:
    def test_copy(self):
        game = Game(players={"key" : "value"})
        copy = game.copy()
        assert copy == game and copy is not game

這是具有等效測試用例的新型類:

from __future__ import annotations

from typing import Dict
import attrs
import cattrs

converter = cattrs.Converter()

@attrs.define
class Game:
    players: Dict[str, str]
    def copy(self) -> Game:
        return converter.structure(converter.unstructure(self), Game)

class TestGame:
    def test_copy(self):
        game = Game(players={"key" : "value"})
        copy = game.copy()
        assert copy == game and copy is not game

這失敗並出現以下錯誤:

test_sample.py:15 (TestGame.test_copy)
self = <tests.test_sample.TestGame object at 0x108ab2490>

    def test_copy(self):
        game = Game(players={"key" : "value"})
>       copy = game.copy()

test_sample.py:18: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
test_sample.py:13: in copy
    return converter.structure(converter.unstructure(self), Game)
../.venv/lib/python3.9/site-packages/cattrs/converters.py:281: in structure
    return self._structure_func.dispatch(cl)(obj, cl)
../.venv/lib/python3.9/site-packages/cattrs/converters.py:446: in structure_attrs_fromdict
    conv_obj[name] = self._structure_attribute(a, val)
../.venv/lib/python3.9/site-packages/cattrs/converters.py:422: in _structure_attribute
    return self._structure_func.dispatch(type_)(value, type_)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <cattrs.converters.Converter object at 0x108a9d6d0>, _ = {'key': 'value'}
cl = 'Dict[str, str]'

    def _structure_error(self, _, cl):
        """At the bottom of the condition stack, we explode if we can't handle it."""
        msg = "Unsupported type: {0!r}. Register a structure hook for " "it.".format(cl)
>       raise StructureHandlerNotFoundError(msg, type_=cl)
E       cattrs.errors.StructureHandlerNotFoundError: Unsupported type: 'Dict[str, str]'. Register a structure hook for it.

../.venv/lib/python3.9/site-packages/cattrs/converters.py:344: StructureHandlerNotFoundError

似乎正在發生的事情是 cattrs 不明白players字段是新樣式類的映射類型字段。

看起來你遇到了這個問題 有兩種解決方法:

  1. 不要使用from __future__ import annotations 您的代碼變為:

     import attrs import cattrs converter = cattrs.Converter() @attrs.define class Game: players: dict[str, str] def copy(self) -> "Game": return converter.structure(converter.unstructure(self), Game) class TestGame: def test_copy(self): game = Game(players={"key": "value"}) copy = game.copy() assert copy == game and copy is not game

    這很丑陋,無論如何都可能在未來打破。

  2. 使用GenConverter而不是Converter

     from __future__ import annotations import attrs import cattrs converter = cattrs.GenConverter() @attrs.define class Game: players: dict[str, str] def copy(self) -> Game: return converter.structure(converter.unstructure(self), Game) class TestGame: def test_copy(self): game = Game(players={"key": "value"}) copy = game.copy() assert copy == game and copy is not game

    這可能是正確的解決方案。

您的示例代碼使用此答案中提供的兩個版本的代碼都通過了測試(在 Python 3.10.4 下測試時)。

(您會注意到我在這里使用dict而不是typing.Dict ;這只是個人喜好,代碼可以使用任何一種方式。)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM