'Creating value type that raises exception on reading
How do I create a value type that raises an exception when read?
For example:
from dataclasses import dataclass, field
Missing = ...
@dataclass
class A:
a: int = field(default=None) # <- value can be None
b: int = field(default=Missing) # <- can be Missing until you try to access it
def print(self):
for i in [self.a, self.b]:
print(i) # <- raises ValueError if i is Missing
Solution 1:[1]
In Python, it seems there is always a way for anything :-)
It appears you can solve this by a clever use of a descriptor value as a dataclass field, as illustrated below. I would also read more on the section on Validators to understand a little bit more about how descriptors work.
from dataclasses import dataclass
# create `_MissingType` class
_MissingType = type('_MissingType', (), {'__bool__': lambda self: False})
# create a singleton for that class
Missing = _MissingType()
class MissingValidator:
__slots__ = ('default', 'private_name')
# You may or may not want a default value
def __init__(self, default=Missing):
self.default = default
def __set_name__(self, owner, name):
self.private_name = '_' + name
# override __get__() to return a default value if one is not passed in to __init__()
def __get__(self, obj, obj_type=None):
try:
value = getattr(obj, self.private_name)
if value is Missing:
cls_name = obj_type.__qualname__
public_name = self.private_name.lstrip('_')
raise ValueError(f'Missing value for field `{public_name}` in class `{cls_name}`')
return value
except AttributeError:
return self.default
def __set__(self, obj, value):
setattr(obj, self.private_name, value)
@dataclass
class A:
a: int = None # <- value can be None
b: int = MissingValidator() # <- can be Missing until you try to access it
def print(self):
for i in [self.a, self.b]:
print(i) # <- raises ValueError if i is Missing
A(b=3).print()
# None
# 3
A(a=42).print()
# raises:
# ValueError: Missing value for field `b` in class `A`
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
