I'd like to chime in with an example of how PEP 563 breaks code that uses dataclasses. I've written a library instant_api (https://github.com/alexmojaki/instant_api) that is heavily inspired by FastAPI but uses dataclasses for complex types instead of pydantic. The example at the beginning of the README is short and demonstrates it nicely. Basically it lets you write code on both the client and server sides that work seamlessly with standard dataclasses, type hints, and type checkers without any plugins, instead of untyped dicts parsed from the JSON that is communicated behind the scenes. `from __future__ import annotations` breaks that README example, even though there are no locally defined types, because as mentioned the dataclass field now contains a string instead of a type. Going a bit deeper, instant_api is powered by https://github.com/alexmojaki/datafunctions, which is more generic than instant_api so that others can build similar tools. Again, the idea is that you can write code with nice dataclasses and type hints, but call it with basic JSON serializable types like dicts. For example: ``` from dataclasses import dataclass from datafunctions import datafunction @dataclass class Point: x: int y: int @datafunction def translate(p: Point, dx: int, dy: int) -> Point: return Point(p.x + dx, p.y + dy) assert translate({"x": 1, "y": 2}, 3, 4) == {"x": 4, "y": 6} # This is equivalent to the following without @datafunction # assert translate(Point(1, 2), 3, 4) == Point(4, 6) ``` In the same way as before, `from __future__ import annotations` breaks this code. The reason is that datafunctions itself is powered by https://github.com/lovasoa/marshmallow_dataclass. Here's an example: ``` from dataclasses import dataclass from marshmallow_dataclass import class_schema @dataclass class Point: x: int y: int schema = class_schema(Point)() assert schema.load({"x": 1, "y": 2}) == Point(1, 2) ``` Again, in the same way as before, `from __future__ import annotations` breaks this code. Specifically `class_schema(Point)` breaks trying to deal with the string `'int'` instead of a type. This problem was raised in https://github.com/lovasoa/marshmallow_dataclass/issues/13 two years ago. It's by far the oldest open issue in the repo. It was clear from the beginning that it's a difficult problem to solve. Little progress has been made, there's one PR that's not in good shape, and it seems there's been no activity there for a while. A couple of other issues have been closed as duplicates. One of those issues is about being unable to use recursive types at all. marshmallow_dataclass has 266 stars. It builds on https://github.com/marshmallow-code/marshmallow, an extremely popular and important data (de)serialization and validation library. Here's a little timeline: - 2013: marshmallow 0.1.0 first released in 2013 - 2014: marshmallow 1.0.0 released - 2015: attrs (precursor to dataclasses) first released - 2016: Python 3.6.0 final released, allowing the variable annotations which make pydantic and dataclasses possible. - 2017: First version of pydantic released - 2018: Python 3.7.0 final released, introducing dataclasses Nowadays pydantic is the natural successor/alternative to marshmallow - Google autocompletes "pydantic vs " with marshmallow as the first option, and vice versa. But marshmallow is clearly well established and entrenched, and thanks to marshmallow_dataclass it was the better fit for my particular use case just last year when I made instant_api. If someone wants to keep combining dataclasses and marshmallow, but without marshmallow_dataclass (e.g. if PEP 563 goes through before marshmallow_dataclass is ready) then they need to revert to the raw marshmallow API which doesn't use type hints. The previous example becomes much uglier: ``` from dataclasses import dataclass from marshmallow import Schema, fields, post_load @dataclass class Point: x: int y: int class PointSchema(Schema): x = fields.Int() y = fields.Int() @post_load def make_point(self, data, **kwargs): return Point(**data) schema = PointSchema() assert schema.load({"x": 1, "y": 2}) == Point(1, 2) ``` This post turned out longer than I initially planned! In summary, my point is that type hints and dataclasses as they work right now make it possible to write some really nice code - nice for humans to both write and read, nice for type checkers and other static analysis, and providing very nice features using annotations at runtime. And despite clear demand and benefits and ample time, people haven't managed to make this code continue working with stringified type annotations. Clearly doing so is not easy. So there's a good case for the dataclasses module to resolve these annotations to actual types, especially if PEP 563 goes through but even if it doesn't.