I've written a new PEP. Please find it below. Happy reading!
Interesting! I like the clever lazy-evaluation of the __annotations__ using a pre-set code object. My only real reservation is that the transition process will be weird but I don't have much to offer in terms of how to smooth it out. I have two questions though:
1. What do you anticipate the memory usage will look like for your solution compared to PEP 563?
To give you an example, EdgeDB is a sizeable application with 100k SLOC of Python. It's got around 3,500 typed functions, all in all >71% type coverage. EdgeDB uses stringified annotations exclusively which minimizes runtime memory usage of annotations because those strings are pretty much all ASCII and many can be interned. Does it matter? It does, actually. Let's look at 20 most popular annotations in the codebase and how often they appear:
946 -> s_schema.Schema
362 -> str
298 -> sd.CommandContext
118 -> base.PLBlock
107 -> irast.Set
99 -> CommandContext
95 -> Any
86 -> qlast.DDLOperation
85 -> s_types.Type
71 -> bool
70 -> irast.PathId
67 -> int
54 -> context.Environment
46 -> so.Object
45 -> pgast.Query
42 -> uuid.UUID
38 -> irast.Base
38 -> sn.Name
37 -> pgast.SelectStmt
33 -> context.ReplContext
(this list tapers of with a long tail after)
Turns out most annotations are simple and predictable. (As a side note: we could make interning even stronger for this purpose if we allowed periods and square brackets for interning.)
2. What is your expected startup performance of an annotated Python application using co_annotations?
The stringification process which your PEP describes as costly only happens during compilation of a .py file to .pyc. Since pip-installing pre-compiles modules for the user at installation time, there is very little runtime penalty for a fully annotated application.
Cheers,
Ł