Created
March 5, 2023 06:21
-
-
Save noamraph/0f83708ab32acd2ccf71fe7a824b891c to your computer and use it in GitHub Desktop.
Trying to make sentinels which seem like the type of themselves
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from __future__ import annotations | |
from typing import Any | |
from abc import ABCMeta | |
# Quit is an example of a sentinel. | |
# I want "class Quit(Sentinel): pass" to produce the same thing. | |
class QuitType(ABCMeta): | |
def __repr__(self) -> str: | |
return "Quit" | |
class Quit(metaclass=QuitType): | |
def __init__(self) -> None: | |
raise AssertionError("Class Quit should not be instantiated") | |
# Note: here is a sentinel, NotImplemented, that I don't know how to mark as a valid return value. | |
@classmethod | |
def __subclasshook__(cls, C: type) -> Any: | |
if C is QuitType: | |
return True | |
else: | |
return NotImplemented | |
def test_quit() -> None: | |
assert isinstance(Quit, Quit) | |
assert repr(Quit) == "Quit" | |
# noinspection PyTypeChecker | |
assert not isinstance(3, Quit) | |
# noinspection PyUnusedLocal | |
class SentinelMeta(ABCMeta): | |
def __new__(mcs, name, bases, namespace) -> type: | |
# print(f"SentinelMeta.__new__({cls!r}, {name!r}, {bases!r}, {namespace!r})") | |
if name == "Sentinel" and namespace["__module__"] == __name__: | |
# This is the Sentinel class, just delegate to our super | |
return ABCMeta.__new__(mcs, name, bases, namespace) | |
assert bases == (Sentinel,) | |
# Create the XType class (QuitType, for example) | |
def __repr__(self) -> str: | |
_ = self | |
return name | |
XType = type( | |
f"{name}Type", | |
(ABCMeta,), | |
{"__module__": namespace["__module__"], "__repr__": __repr__}, | |
) | |
# Create the X class (Quit, for example) | |
def __init__(self) -> None: | |
raise AssertionError(f"Class {name} should not be instantiated") | |
# noinspection PyDecorator | |
@classmethod | |
def __subclasshook__(cls, C: type) -> Any: | |
if C is XType: | |
return True | |
else: | |
return NotImplemented | |
X = XType( | |
name, | |
(), | |
namespace | {"__init__": __init__, "__subclasshook__": __subclasshook__}, | |
) | |
return X | |
class Sentinel(metaclass=SentinelMeta): | |
pass | |
def test_sentinel() -> None: | |
class MySentinel(Sentinel): | |
pass | |
assert isinstance(MySentinel, MySentinel) | |
assert repr(MySentinel) == "MySentinel" | |
# noinspection PyTypeChecker | |
assert not isinstance(Quit, MySentinel) | |
assert not isinstance(MySentinel, Quit) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment