Created
March 26, 2021 03:31
-
-
Save TerrorBite/0443df60f85c52325282a08cd8a7b882 to your computer and use it in GitHub Desktop.
This file contains 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 typing import Dict, Any, Type, TypeVar, Optional, Sequence, Union | |
from contextlib import contextmanager | |
T = TypeVar | |
class NamedConstantMeta(type): | |
def __init__(cls, *args, **kwargs): | |
super().__init__(*args, **kwargs) | |
#: This class attribute will be the dict mapping constant names to their constants. | |
cls._constants: Dict[str, Any] = {} | |
def __getitem__(cls: Type[T], name: str) -> T: | |
# This will naturally raise a KeyError if the constant does not exist | |
return cls._constants[name] | |
def __contains__(cls, name: str) -> bool: | |
return name in cls._constants | |
class NamedConstant(metaclass=NamedConstantMeta): | |
""" | |
Each instance of this class can be used as a constant. | |
Create a constant by passing the name, e.g. ``MYCONST = NamedConstant('MYCONST')``. | |
Attempting to create a duplicate will raise a `ValueError`. | |
Constants are equal only to themselves; in other words, the | |
expression ``x == const`` is equivalent to ``x is const``. | |
The name of a constant can be retrieved using the name attribute. | |
Existing constants can be retrieved by name using the item accessor syntax with the class; | |
for example, ``NamedConstant['SOME_CONSTANT']``. This is useful for retrieving constants | |
which have been serialised by name. A `KeyError` will be raised if there is no such constant. | |
You can check if a constant exists the standard way, e.g. ``'SOME_CONSTANT' in NamedConstant``. | |
:param name: The name of the constant to create. | |
:raises ValueError: if there is already a constant with that name. | |
""" | |
def __init__(self, name: str): | |
if name in self._constants: | |
raise ValueError(f"There is already a NamedConstant called {name!r}") | |
self._constants[name] = self | |
self.__name__: str = name | |
@property | |
def name(self) -> str: | |
return self.__name__ | |
def __eq__(self, other): | |
return other is self | |
def __hash__(self): | |
return id(self) | |
@contextmanager | |
def should_raise(exc_type: Type[BaseException], reason: Optional[str]=None): | |
""" | |
Used for testing, to assert that an exception is raised. | |
:param exc_type: Expect this type of exception. | |
:param reason: Optional. If provided, check that the string value of the exception is this string. | |
:raises AssertionError: If the code does not raise the expected exception, or if a reason was provided but the | |
text of the raised exception doesn't match. | |
""" | |
try: | |
yield None | |
except exc_type as e: # The expected exception was raised. Yay! | |
if reason is not None and str(e) != reason: | |
raise AssertionError( | |
f"Exception didn't have the expected message\n Actual: {str(e)!r}\nExpected: {reason!r}" | |
) from e | |
except BaseException as e: # Catch even things like SystemExit, this is intentional | |
raise AssertionError(f"Expected {exc_type!r}, but {type(e)!r} was raised instead") from e | |
else: # Code didn't raise any exception at all | |
raise AssertionError(f"Expected {exc_type!r}, but no exception was raised") | |
if __name__ == '__main__': | |
# Create two constants for testing purposes | |
const1 = NamedConstant('const1') | |
const2 = NamedConstant('const2') | |
# Make sure duplicates can't be created | |
with should_raise(ValueError, "There is already a NamedConstant called 'const1'"): | |
NamedConstant('const1') | |
# Test identity | |
assert const1 is const1 | |
assert const1 is not const2 | |
# Test equality | |
assert const1 == const1 | |
assert const1 != const2 | |
assert const1 != 'const1' | |
# Test that the name can be retrieved | |
assert const1.name == 'const1' | |
# Test that constants can be retrieved by name | |
assert NamedConstant['const1'] is const1 | |
with should_raise(KeyError, "'nonexistent'"): | |
nonexistent = NamedConstant['nonexistent'] | |
# Test that you can check if constants exist | |
assert 'const1' in NamedConstant | |
assert 'const3' not in NamedConstant | |
# Check that constants are hashable (the hash should be the ID) | |
assert hash(const1) == id(const1) | |
# Check that constants are usable as keys, and don't collide with their names | |
d = {const1: 1, const2: 2, 'const1': 3} | |
assert d[const1] == 1 | |
assert d[const2] == 2 | |
assert d['const1'] == 3 | |
print("All tests succeeded") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment