expe: undefined python class
This commit is contained in:
parent
362d6321ef
commit
7d519ff647
|
@ -1,117 +1,64 @@
|
||||||
# Example of an undefined singleton, usable with eval() functions to limit
|
from functools import total_ordering
|
||||||
# possibility of NameError exceptions.
|
|
||||||
#
|
|
||||||
# Use with a namespace decorator like :
|
|
||||||
#
|
|
||||||
# class NoNameErrorNamespace:
|
|
||||||
# def __init__(self, ns):
|
|
||||||
# self.ns = ns
|
|
||||||
#
|
|
||||||
# def __getitem__(self, name):
|
|
||||||
# try:
|
|
||||||
# return self.ns[name]
|
|
||||||
# except KeyError:
|
|
||||||
# return Undefined()
|
|
||||||
#
|
|
||||||
# def __setitem__(self, name, value):
|
|
||||||
# self.ns[name] = value
|
|
||||||
#
|
|
||||||
# eval('x - 1')
|
|
||||||
|
|
||||||
|
|
||||||
class Undefined:
|
def init_undefined():
|
||||||
def __str__(self):
|
singleton = None
|
||||||
return 'Undefined'
|
|
||||||
|
|
||||||
def __getattr__(self, name):
|
@total_ordering
|
||||||
return self
|
class Undefined:
|
||||||
|
def __new__(cls, *args, **kwargs):
|
||||||
|
return singleton
|
||||||
|
|
||||||
def __neq__(self, other):
|
def __call__(self, *args, **kwargs):
|
||||||
return True
|
return singleton
|
||||||
|
|
||||||
self = lambda self, *args, **kwargs: self
|
def __getattribute__(self, name):
|
||||||
true = lambda *args: True
|
return singleton
|
||||||
false = lambda *args: False
|
|
||||||
|
|
||||||
__eq__ = self
|
def __add__(self, other):
|
||||||
__lt__ = false
|
return singleton
|
||||||
__gt__ = false
|
|
||||||
__le__ = false
|
|
||||||
__ge__ = false
|
|
||||||
|
|
||||||
# Arithmetic operators
|
def __sub__(self, other):
|
||||||
for op in ['__add__', '__sub__', '__mul__', '__matmul__', '__truediv__',
|
return singleton
|
||||||
'__floordiv__', '__mod__', '__divmod__', '__pow__',
|
|
||||||
'__lshift__', '__rshift__', '__and__', '__xor__', '__or__']:
|
|
||||||
locals()[op] = self
|
|
||||||
locals()[op.replace('__', '__r', 1)] = self
|
|
||||||
locals()[op.replace('__', '__i', 1)] = self
|
|
||||||
|
|
||||||
__call__ = self
|
def __eq__(self, other):
|
||||||
__getitem__ = self
|
return singleton
|
||||||
__bool__ = false
|
|
||||||
__abs__ = self
|
|
||||||
__neg__ = self
|
|
||||||
__pos__ = self
|
|
||||||
__invert__ = self
|
|
||||||
__contains__ = self
|
|
||||||
__reversed__ = self
|
|
||||||
__length_hint__ = lambda: 0
|
|
||||||
__hash__ = lambda self: id(self)
|
|
||||||
|
|
||||||
__instance = None
|
def __req__(self, other):
|
||||||
def __new__(cls, *args, **kwargs):
|
return singleton
|
||||||
if cls.__instance is None:
|
|
||||||
cls.__instance = super().__new__(cls)
|
def __lt__(self, other):
|
||||||
return cls.__instance
|
return singleton
|
||||||
|
|
||||||
|
def __bool__(self):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def __getitem__(self, name):
|
||||||
|
return singleton
|
||||||
|
|
||||||
|
def __setitem__(self, name, value):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def __setattr__(self, name, value):
|
||||||
|
pass
|
||||||
|
|
||||||
|
singleton = object.__new__(Undefined)
|
||||||
|
return Undefined
|
||||||
|
|
||||||
|
|
||||||
import operator
|
Undefined = init_undefined()
|
||||||
import inspect
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
|
undefined = Undefined()
|
||||||
|
|
||||||
def test():
|
assert undefined is Undefined()
|
||||||
assert Undefined().__lt__(1) is False
|
|
||||||
|
|
||||||
assert Undefined() is Undefined()
|
assert Undefined() + Undefined() is Undefined()
|
||||||
assert Undefined().x is Undefined()
|
assert (Undefined() == 1) is Undefined()
|
||||||
assert Undefined()['a'] is Undefined()
|
assert not bool(Undefined())
|
||||||
assert Undefined()() is Undefined()
|
assert undefined.a is undefined
|
||||||
assert Undefined().x('a', 1, x=3) is Undefined()
|
assert undefined.a.b.c is undefined
|
||||||
assert not Undefined()
|
assert undefined.a.b.c.d() is undefined
|
||||||
assert not bool(Undefined())
|
assert undefined[undefined] is undefined
|
||||||
assert (Undefined() - 1) is Undefined()
|
undefined[1] = 0
|
||||||
assert (1 - Undefined()) is Undefined()
|
undefined.x = 1
|
||||||
|
assert undefined.x is undefined
|
||||||
|
|
||||||
ops = [op for key, op in operator.__dict__.items() if callable(op) and key.startswith('__')]
|
|
||||||
@pytest.mark.parametrize('obj', [1, '', [], (), {}, dict()], ids=['integer', 'string', 'list', 'tuple', 'set', 'dict'])
|
|
||||||
@pytest.mark.parametrize('op', ops, ids=[op.__name__ for op in ops])
|
|
||||||
def test_op(op, obj):
|
|
||||||
if isinstance(op, type):
|
|
||||||
pytest.skip()
|
|
||||||
name = op.__name__.strip('_').strip('0')
|
|
||||||
if name in ['index', 'concat', 'countOf', 'delitem', 'indexOf', 'setitem', 'attrgetter', 'iconcat']:
|
|
||||||
pytest.skip()
|
|
||||||
args_len = len([p for p in inspect.signature(op).parameters.values() if p.default is p.empty])
|
|
||||||
if args_len == 0:
|
|
||||||
pytest.skip('no arg')
|
|
||||||
result = Undefined()
|
|
||||||
if name in ['lt', 'gt', 'le', 'ge', 'truth', 'is', 'contains']:
|
|
||||||
result = False
|
|
||||||
if name in ['ne', 'not', 'is_not']:
|
|
||||||
result = True
|
|
||||||
if name in ['length_hint']:
|
|
||||||
result = 0
|
|
||||||
result_reversed = result
|
|
||||||
if name in ['imod', 'mod'] and obj == '':
|
|
||||||
result_reversed = ''
|
|
||||||
if args_len == 1:
|
|
||||||
assert op(Undefined()) is result, f'failed with {op}'
|
|
||||||
elif args_len == 2:
|
|
||||||
assert op(Undefined(), obj) is result, f'failed with {op}'
|
|
||||||
if name not in ['contains', 'getitem'] or (name == 'ctonains' and obj != '' and hasattr(obj, '__contains__')) or (name == 'getitem' and hasattr(obj, '__getitem__') and obj != '' and obj != [] and obj != () and obj != {}):
|
|
||||||
assert op(obj, Undefined()) is result_reversed, f'failed with reversed {op}'
|
|
||||||
else:
|
|
||||||
raise NotImplementedError(f'{op} {args_len}')
|
|
||||||
|
|
Loading…
Reference in New Issue