|
38 | 38 | )
|
39 | 39 | from types import FunctionType, NoneType
|
40 | 40 | from typing import (
|
| 41 | + TYPE_CHECKING, |
41 | 42 | Any,
|
42 | 43 | Final,
|
43 | 44 | Literal,
|
@@ -2638,18 +2639,6 @@ def get_displayname(self, i: int) -> str:
|
2638 | 2639 | return f'"argument {i}"'
|
2639 | 2640 |
|
2640 | 2641 |
|
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 |
| - |
2653 | 2642 | CConverterClassT = TypeVar("CConverterClassT", bound=type["CConverter"])
|
2654 | 2643 |
|
2655 | 2644 | def add_c_converter(
|
@@ -2844,16 +2833,28 @@ def __init__(self,
|
2844 | 2833 | if annotation is not unspecified:
|
2845 | 2834 | fail("The 'annotation' parameter is not currently permitted.")
|
2846 | 2835 |
|
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.) |
2854 | 2840 | self.converter_init(**kwargs)
|
2855 | 2841 | self.function = function
|
2856 | 2842 |
|
| 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 | + |
2857 | 2858 | def converter_init(self) -> None:
|
2858 | 2859 | pass
|
2859 | 2860 |
|
@@ -4005,7 +4006,6 @@ def converter_init(self, *, type: str | None = None) -> None:
|
4005 | 4006 |
|
4006 | 4007 | def pre_render(self) -> None:
|
4007 | 4008 | f = self.function
|
4008 |
| - assert isinstance(f, Function) |
4009 | 4009 | default_type, default_name = correct_name_for_self(f)
|
4010 | 4010 | self.signature_name = default_name
|
4011 | 4011 | self.type = self.specified_type or self.type or default_type
|
@@ -4056,7 +4056,6 @@ def pre_render(self) -> None:
|
4056 | 4056 | @property
|
4057 | 4057 | def parser_type(self) -> str:
|
4058 | 4058 | assert self.type is not None
|
4059 |
| - assert isinstance(self.function, Function) |
4060 | 4059 | return required_type_for_self_for_parser(self.function) or self.type
|
4061 | 4060 |
|
4062 | 4061 | def render(self, parameter: Parameter, data: CRenderData) -> None:
|
|
0 commit comments