Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gh-119180: Add VALUE_WITH_FAKE_GLOBALS format to annotationlib #124415

Open
wants to merge 9 commits into
base: main
Choose a base branch
from

Conversation

JelleZijlstra
Copy link
Member

@JelleZijlstra JelleZijlstra commented Sep 24, 2024

Copy link
Contributor

@larryhastings larryhastings left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly this is about "the actual runtime values of the format constants shouldn't be defined as part of the API". I invite you to convince me otherwise.

@@ -144,6 +144,17 @@ Classes

The exact values of these strings may change in future versions of Python.

.. attribute:: VALUE_WITH_FAKE_GLOBALS
:value: 4
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VALUE_WITH_FAKE_GLOBALS is now 2. But why publish their values in the first place?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A wise man wrote these values in PEP 649 (https://peps.python.org/pep-0649/#overview). They are also relevant if you write an annotate function in C. But yes, we can probably do without them in Python code.

@@ -413,19 +416,28 @@ def f2(a: undefined):
annotationlib.get_annotations(f2, format=Format.FORWARDREF),
{"a": fwd},
)
self.assertEqual(annotationlib.get_annotations(f2, format=2), {"a": fwd})
self.assertEqual(annotationlib.get_annotations(f2, format=3), {"a": fwd})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is format hard-coded here? I'd prefer you think of it like an enum, an arbitrary value whose actual value doesn't matter. I don't think it's necessary to externally test that the format constants are specific values; they happen to be 1, 2, 3, 4, but I don't want external users to rely on that or ever hard-code them.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The intent was to test that the function accepts both the integer value and the enum. If you think it's OK to accept only the enum, I can switch to that model.

@@ -495,6 +496,8 @@ def call_annotate_function(annotate, format, *, owner=None, _is_evaluate=False):
on the generated ForwardRef objects.

"""
if format == Format.VALUE_WITH_FAKE_GLOBALS:
raise ValueError("The VALUE_WITH_FAKE_GLOBALS format is for internal use only")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was surprised to see this exception. I haven't given the matter a lot of thought, and I don't have a strongly held opinion yet. Can you tell me why we should raise this exception here? (My knee-jerk instinct: "special cases aren't special enough to break the rules.")

p.s. if we do keep these assertions, please drop "The ". The text should read "VALUES_WITH_FAKE_GLOBALS format is for internal use only".

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no meaningful value to return for VALUE_WITH_FAKE_GLOBALS; it's only useful within an annotate function.

Concretely, raising an exception here means that if a user-provided annotate function delegates to an API in annotationlib without further thought, it will raise an exception for VALUE_WITH_FAKE_GLOBALS, which is what we'd want.

@@ -42,11 +42,14 @@ def test_enum(self):
self.assertEqual(Format.VALUE.value, 1)
self.assertEqual(Format.VALUE, 1)

self.assertEqual(Format.FORWARDREF.value, 2)
self.assertEqual(Format.FORWARDREF, 2)
self.assertEqual(Format.VALUE_WITH_FAKE_GLOBALS.value, 2)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See related comments, I don't think the fact that STRING format is the int object 2 is or should be part of the interface. I don't want people hard-coding it.

@@ -168,7 +168,7 @@ constevaluator_call(PyObject *self, PyObject *args, PyObject *kwargs)
return NULL;
}
PyObject *value = ((constevaluatorobject *)self)->value;
if (format == 3) { // STRING
if (format == 4) { // STRING
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The formats should be defined in C too. Macros are fine. I'm expecting FORMAT_STRING here, optionally with some variant of Py_ in front. Worst case, Py_ANNOTATE_FORMAT_STRING.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants