|
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