[Python-checkins] gh-104683: Argument clinic: remove the `LandMine` class (#107541)
AlexWaygood
webhook-mailer at python.org
Tue Aug 1 16:11:10 EDT 2023
https://github.com/python/cpython/commit/030f6b1e84274616c22666f27c5695867ab85833
commit: 030f6b1e84274616c22666f27c5695867ab85833
branch: main
author: Alex Waygood <Alex.Waygood at Gmail.com>
committer: AlexWaygood <Alex.Waygood at Gmail.com>
date: 2023-08-01T21:10:54+01:00
summary:
gh-104683: Argument clinic: remove the `LandMine` class (#107541)
files:
M Lib/test/test_clinic.py
M Tools/clinic/clinic.py
diff --git a/Lib/test/test_clinic.py b/Lib/test/test_clinic.py
index 5e74b4fb77022..6100444e6da79 100644
--- a/Lib/test/test_clinic.py
+++ b/Lib/test/test_clinic.py
@@ -331,8 +331,7 @@ def converter_init(self):
[clinic start generated code]*/
""")
msg = (
- "Stepped on a land mine, trying to access attribute 'noaccess':\n"
- "Don't access members of self.function inside converter_init!"
+ "accessing self.function inside converter_init is disallowed!"
)
self.assertIn(msg, out)
diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py
index 3501c16a567cc..ba0132fc35903 100755
--- a/Tools/clinic/clinic.py
+++ b/Tools/clinic/clinic.py
@@ -38,6 +38,7 @@
)
from types import FunctionType, NoneType
from typing import (
+ TYPE_CHECKING,
Any,
Final,
Literal,
@@ -2638,18 +2639,6 @@ def get_displayname(self, i: int) -> str:
return f'"argument {i}"'
- at dc.dataclass
-class LandMine:
- # try to access any
- __message__: str
-
- def __getattribute__(self, name: str) -> Any:
- if name in ('__repr__', '__message__'):
- return super().__getattribute__(name)
- # raise RuntimeError(repr(name))
- fail("Stepped on a land mine, trying to access attribute " + repr(name) + ":\n" + self.__message__)
-
-
CConverterClassT = TypeVar("CConverterClassT", bound=type["CConverter"])
def add_c_converter(
@@ -2844,16 +2833,28 @@ def __init__(self,
if annotation is not unspecified:
fail("The 'annotation' parameter is not currently permitted.")
- # this is deliberate, to prevent you from caching information
- # about the function in the init.
- # (that breaks if we get cloned.)
- # so after this change we will noisily fail.
- self.function: Function | LandMine = LandMine(
- "Don't access members of self.function inside converter_init!"
- )
+ # Make sure not to set self.function until after converter_init() has been called.
+ # This prevents you from caching information
+ # about the function in converter_init().
+ # (That breaks if we get cloned.)
self.converter_init(**kwargs)
self.function = function
+ # Add a custom __getattr__ method to improve the error message
+ # if somebody tries to access self.function in converter_init().
+ #
+ # mypy will assume arbitrary access is okay for a class with a __getattr__ method,
+ # and that's not what we want,
+ # so put it inside an `if not TYPE_CHECKING` block
+ if not TYPE_CHECKING:
+ def __getattr__(self, attr):
+ if attr == "function":
+ fail(
+ f"{self.__class__.__name__!r} object has no attribute 'function'.\n"
+ f"Note: accessing self.function inside converter_init is disallowed!"
+ )
+ return super().__getattr__(attr)
+
def converter_init(self) -> None:
pass
@@ -4005,7 +4006,6 @@ def converter_init(self, *, type: str | None = None) -> None:
def pre_render(self) -> None:
f = self.function
- assert isinstance(f, Function)
default_type, default_name = correct_name_for_self(f)
self.signature_name = default_name
self.type = self.specified_type or self.type or default_type
@@ -4056,7 +4056,6 @@ def pre_render(self) -> None:
@property
def parser_type(self) -> str:
assert self.type is not None
- assert isinstance(self.function, Function)
return required_type_for_self_for_parser(self.function) or self.type
def render(self, parameter: Parameter, data: CRenderData) -> None:
More information about the Python-checkins
mailing list