Skip to content

Commit 030f6b1

Browse files
authored
gh-104683: Argument clinic: remove the LandMine class (#107541)
1 parent 2bd04d4 commit 030f6b1

File tree

2 files changed

+21
-23
lines changed

2 files changed

+21
-23
lines changed

Lib/test/test_clinic.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -331,8 +331,7 @@ def converter_init(self):
331331
[clinic start generated code]*/
332332
""")
333333
msg = (
334-
"Stepped on a land mine, trying to access attribute 'noaccess':\n"
335-
"Don't access members of self.function inside converter_init!"
334+
"accessing self.function inside converter_init is disallowed!"
336335
)
337336
self.assertIn(msg, out)
338337

Tools/clinic/clinic.py

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
)
3939
from types import FunctionType, NoneType
4040
from typing import (
41+
TYPE_CHECKING,
4142
Any,
4243
Final,
4344
Literal,
@@ -2638,18 +2639,6 @@ def get_displayname(self, i: int) -> str:
26382639
return f'"argument {i}"'
26392640

26402641

2641-
@dc.dataclass
2642-
class LandMine:
2643-
# try to access any
2644-
__message__: str
2645-
2646-
def __getattribute__(self, name: str) -> Any:
2647-
if name in ('__repr__', '__message__'):
2648-
return super().__getattribute__(name)
2649-
# raise RuntimeError(repr(name))
2650-
fail("Stepped on a land mine, trying to access attribute " + repr(name) + ":\n" + self.__message__)
2651-
2652-
26532642
CConverterClassT = TypeVar("CConverterClassT", bound=type["CConverter"])
26542643

26552644
def add_c_converter(
@@ -2844,16 +2833,28 @@ def __init__(self,
28442833
if annotation is not unspecified:
28452834
fail("The 'annotation' parameter is not currently permitted.")
28462835

2847-
# this is deliberate, to prevent you from caching information
2848-
# about the function in the init.
2849-
# (that breaks if we get cloned.)
2850-
# so after this change we will noisily fail.
2851-
self.function: Function | LandMine = LandMine(
2852-
"Don't access members of self.function inside converter_init!"
2853-
)
2836+
# Make sure not to set self.function until after converter_init() has been called.
2837+
# This prevents you from caching information
2838+
# about the function in converter_init().
2839+
# (That breaks if we get cloned.)
28542840
self.converter_init(**kwargs)
28552841
self.function = function
28562842

2843+
# Add a custom __getattr__ method to improve the error message
2844+
# if somebody tries to access self.function in converter_init().
2845+
#
2846+
# mypy will assume arbitrary access is okay for a class with a __getattr__ method,
2847+
# and that's not what we want,
2848+
# so put it inside an `if not TYPE_CHECKING` block
2849+
if not TYPE_CHECKING:
2850+
def __getattr__(self, attr):
2851+
if attr == "function":
2852+
fail(
2853+
f"{self.__class__.__name__!r} object has no attribute 'function'.\n"
2854+
f"Note: accessing self.function inside converter_init is disallowed!"
2855+
)
2856+
return super().__getattr__(attr)
2857+
28572858
def converter_init(self) -> None:
28582859
pass
28592860

@@ -4005,7 +4006,6 @@ def converter_init(self, *, type: str | None = None) -> None:
40054006

40064007
def pre_render(self) -> None:
40074008
f = self.function
4008-
assert isinstance(f, Function)
40094009
default_type, default_name = correct_name_for_self(f)
40104010
self.signature_name = default_name
40114011
self.type = self.specified_type or self.type or default_type
@@ -4056,7 +4056,6 @@ def pre_render(self) -> None:
40564056
@property
40574057
def parser_type(self) -> str:
40584058
assert self.type is not None
4059-
assert isinstance(self.function, Function)
40604059
return required_type_for_self_for_parser(self.function) or self.type
40614060

40624061
def render(self, parameter: Parameter, data: CRenderData) -> None:

0 commit comments

Comments
 (0)