Skip to content

Instantly share code, notes, and snippets.

@noamraph
Created March 5, 2023 06:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save noamraph/0f83708ab32acd2ccf71fe7a824b891c to your computer and use it in GitHub Desktop.
Save noamraph/0f83708ab32acd2ccf71fe7a824b891c to your computer and use it in GitHub Desktop.
Trying to make sentinels which seem like the type of themselves
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