Repository navigation
How to convert annotations to Format.SOURCE in __annotate__? #124412
Copy link
Copy link
Closed
Labels
Description
Feature or enhancement
Let's say you have a dict of some custom annotations like I have in #122262
How users are expected to convert say an annotation dict of {'user': CustomUser[AuthToken], 'auth_callback': Callable[[CustomUser[T]], T]} to string?
There are several ways right now:
reprfor simple types, which might not work for complex ones- but, it requires complex
Lines 466 to 501 in 536bc8a
if format == Format.SOURCE: # SOURCE is implemented by calling the annotate function in a special # environment where every name lookup results in an instance of _Stringifier. # _Stringifier supports every dunder operation and returns a new _Stringifier. # At the end, we get a dictionary that mostly contains _Stringifier objects (or # possibly constants if the annotate function uses them directly). We then # convert each of those into a string to get an approximation of the # original source. globals = _StringifierDict({}) if annotate.__closure__: freevars = annotate.__code__.co_freevars new_closure = [] for i, cell in enumerate(annotate.__closure__): if i < len(freevars): name = freevars[i] else: name = "__cell__" fwdref = _Stringifier(ast.Name(id=name)) new_closure.append(types.CellType(fwdref)) closure = tuple(new_closure) else: closure = None func = types.FunctionType( annotate.__code__, globals, closure=closure, argdefs=annotate.__defaults__, kwdefaults=annotate.__kwdefaults__, ) annos = func(Format.VALUE) if _is_evaluate: return annos if isinstance(annos, str) else repr(annos) return { key: val if isinstance(val, str) else repr(val) for key, val in annos.items() } annotateobject - but it is a private API
Lines 2955 to 2956 in 536bc8a
def _convert_to_source(types): return {n: t if isinstance(t, str) else _type_repr(t) for n, t in types.items()}
I propose adding a public and documented API for that.
Linked PRs
Reactions are currently unavailable
Activity
JelleZijlstra commentedon Sep 24, 2024
The use cases for this would be cases where types get provided outside annotations (e.g., with the functional syntax for
NamedTupleandTypedDict, and withmake_dataclass), and you want the__annotate__method to support the SOURCE format.The implementation is
repr()for most cases, but for types we want the fully qualified name instead of<type 'int'>, and there are a few other special cases. Currently we havetyping._convert_to_source, which takes an annotations dict and usestyping._type_reprto repr each annotation.Since we already have use cases from two standard library modules (typing and dataclasses), I think it makes sense to add something to
annotationlib. I would suggest:annotationlib.repr_annotations(dict), similar to the currenttyping._convert_sourceannotationlib.repr_type(type), which works liketyping._type_reprbut without the special case for tuplessobolevn commentedon Sep 24, 2024
I want to investigate on this feature, because I still have a limited understanding of
__annotate__and all of its corner-cases.Will probably work on this tomorrow 👍
JelleZijlstra commentedon Sep 24, 2024
Thanks! We'll also want to add the new functions to PEP 749.
JelleZijlstra commentedon Sep 25, 2024
We also need this in annotationlib itself, for the edge case where a user-created object has
__annotations__but not__annotate__. Currently, in that case SOURCE returns non-strings:I think the most reasonable behavior for this case is to return
{'x': 'int'}.pythongh-124412: Add helpers for converting annotations to source format
larryhastings commentedon Sep 25, 2024
My initial reaction is, this seems like a least-worst option, and it may even be the least-worst option. I'd like to marinate on it further. I won't mind too much if you go ahead and merge without my blessing--as long as you don't mind me retroactively pushing back if I (eventually) arrive at some other conclusion.
larryhastings commentedon Sep 25, 2024
I will say, shenanigans like this are an argument against calling this format
SOURCE.gh-124412: Add helpers for converting annotations to source format (#…
Merge branch 'main' into fix-help-double-prompt
sobolevn commentedon Sep 26, 2024
@JelleZijlstra can this be closed now? Sorry, I was not quick enough to send a PR :( It was only partially ready yesterday, due to some other work.
JelleZijlstra commentedon Sep 26, 2024
Let's leave it open for a while to allow Larry to ruminate.
larryhastings commentedon Sep 26, 2024
Jelle has convinced himself that we should change the name away from
SOURCE, I think he preferredSTRINGSwhich is fine.It's a day later and this approach seems like a good idea. Ship it! We can always regret it later.
JelleZijlstra commentedon Sep 27, 2024
To be precise I propose
STRING, since the other formats are also in the singular.larryhastings commentedon Sep 29, 2024
STRINGis fine by me. My only counter-proposal isTEXT, which is also sufficiently singular. I don't have a strong opinion about it either way; I don't thinkTEXTdoes any better job of conveying the intended semantics of the format.