diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py index ebee9782939499..42aac7eb17f2ff 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,26 @@ "return_value", } -class Unspecified: + +class Sentinels(enum.Enum): + unspecified = "unspecified" + unknown = "unknown" + def __repr__(self) -> str: - return '' + return f"<{self.value.capitalize()}>" + -unspecified = Unspecified() +unspecified: Final = Sentinels.unspecified +unknown: Final = Sentinels.unknown +# This one needs to be a distinct class, unlike the other two class Null: def __repr__(self) -> str: return '' -NULL = Null() - -class Unknown: - def __repr__(self) -> str: - return '' - -unknown = Unknown() +NULL = Null() sig_end_marker = '--' @@ -2600,7 +2602,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: object = unspecified # If not None, default must be isinstance() of this type. # (You can also specify a tuple of types.) @@ -2686,11 +2688,11 @@ def __init__(self, name: str, py_name: str, function, - default=unspecified, + default: object = unspecified, *, # 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 +2701,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: @@ -2713,7 +2718,7 @@ def __init__(self, if py_default: self.py_default = py_default - if annotation != unspecified: + if annotation is not unspecified: fail("The 'annotation' parameter is not currently permitted.") # this is deliberate, to prevent you from caching information @@ -3967,7 +3972,7 @@ class CReturnConverter(metaclass=CReturnConverterAutoRegister): # The Python default value for this parameter, as a Python value. # Or the magic value "unspecified" if there is no default. - default = None + default: object = None def __init__(self, *, py_default=None, **kwargs): self.py_default = py_default @@ -4767,7 +4772,7 @@ def bad_node(self, node): # but at least make an attempt at ensuring it's a valid expression. try: value = eval(default) - if value == unspecified: + if value is unspecified: fail("'unspecified' is not a legal default value!") except NameError: pass # probably a named constant