diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py index 5fcf2bf485fc8a..f455da597f2441 100755 --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -12,6 +12,7 @@ import contextlib import copy import cpp +import enum import functools import hashlib import inspect @@ -28,7 +29,7 @@ from collections.abc import Callable from types import FunctionType, NoneType -from typing import Any, NamedTuple, NoReturn, Literal, overload +from typing import Any, Final, NamedTuple, NoReturn, Literal, overload # TODO: # @@ -58,25 +59,22 @@ "return_value", } -class Unspecified: - def __repr__(self) -> str: - return '' - -unspecified = Unspecified() +class Sentinels(enum.Enum): + unspecified = "unspecified" + NULL = "null" + unknown = "unknown" -class Null: def __repr__(self) -> str: - return '' + return f"<{self.value.capitalize()}>" -NULL = Null() +unspecified: Final = Sentinels.unspecified +NULL: Final = Sentinels.NULL +unknown: Final = Sentinels.unknown -class Unknown: - def __repr__(self) -> str: - return '' +NullType = type(Sentinels.NULL) -unknown = Unknown() sig_end_marker = '--' @@ -2600,7 +2598,7 @@ class CConverter(metaclass=CConverterAutoRegister): # Or the magic value "unknown" if this value is a cannot be evaluated # at Argument-Clinic-preprocessing time (but is presumed to be valid # at runtime). - default: bool | Unspecified = unspecified + default: bool | Literal[Sentinels.unspecified] = unspecified # If not None, default must be isinstance() of this type. # (You can also specify a tuple of types.) @@ -2690,7 +2688,7 @@ def __init__(self, *, # Keyword only args: c_default: str | None = None, py_default: str | None = None, - annotation: str | Unspecified = unspecified, + annotation: str | Literal[Sentinels.unspecified] = unspecified, unused: bool = False, **kwargs ): @@ -2699,7 +2697,10 @@ def __init__(self, self.unused = unused if default is not unspecified: - if self.default_type and not isinstance(default, (self.default_type, Unknown)): + if (self.default_type + and default is not unknown + and not isinstance(default, self.default_type) + ): if isinstance(self.default_type, type): types_str = self.default_type.__name__ else: @@ -3487,7 +3488,7 @@ def str_converter_key(types, encoding, zeroes): class str_converter(CConverter): type = 'const char *' - default_type = (str, Null, NoneType) + default_type = (str, NullType, NoneType) format_unit = 's' def converter_init( @@ -3506,7 +3507,7 @@ def converter_init( self.format_unit = format_unit self.length = bool(zeroes) if encoding: - if self.default not in (Null, None, unspecified): + if self.default not in (NULL, None, unspecified): fail("str_converter: Argument Clinic doesn't support default values for encoded strings") self.encoding = encoding self.type = 'char *' @@ -3654,7 +3655,7 @@ def parse_arg(self, argname: str, displayname: str) -> str: class unicode_converter(CConverter): type = 'PyObject *' - default_type = (str, Null, NoneType) + default_type = (str, NullType, NoneType) format_unit = 'U' def parse_arg(self, argname: str, displayname: str) -> str: @@ -3678,7 +3679,7 @@ def parse_arg(self, argname: str, displayname: str) -> str: @add_legacy_c_converter('Z#', accept={str, NoneType}, zeroes=True) class Py_UNICODE_converter(CConverter): type = 'const Py_UNICODE *' - default_type = (str, Null, NoneType) + default_type = (str, NullType, NoneType) def converter_init( self, *,