[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