#!/usr/bin/env python
# -*- coding: utf-8 -*-
# ---------------------------------------------------------------------
# Copyright (c) Merchise Autrement [~º/~] and Contributors
# All rights reserved.
#
# This is free software; you can do what the LICENCE file allows you to.
#
'''Special logical values like Unset, Undefined, Ignored, Invalid, ...
All values only could be `True` or `False` but are intended in places where
`None` is expected to be a valid value or for special Boolean formats.
'''
from __future__ import (division as _py3_division,
print_function as _py3_print,
absolute_import as _py3_abs_imports)
from xoutil.eight.meta import metaclass
SYMBOL = 'symbol'
BOOLEAN = 'boolean'
TIMEOUT = 2.0
[docs]class symbol(metaclass(MetaSymbol), int):
'''Instances are custom symbols.
See `~MetaSymbol.__getitem__`:meth: operator for information on
constructor arguments.
For example::
>>> ONE2MANY = symbol('ONE2MANY')
>>> ONE_TO_MANY = symbol('ONE2MANY')
>>> ONE_TO_MANY ONE2MANY
True
'''
__slots__ = ()
def __new__(cls, name, value=None):
'''Get or create a new symbol instance.
:param name: String representing the internal name. `Symbol`:class:
instances are unique (singletons) in the context of this
argument. ``#`` and spaces are invalid characters to allow
comments.
:param value: Any value compatible with Python `bool` or `int` types.
`None` is used as a special value to create a value using the
name hash.
'''
from xoutil.eight import intern as unique, type_name
name = unique(name)
if name:
valid = {symbol: lambda v: isinstance(v, int),
boolean: lambda v: v is False or v is True}
with cls._instances as cache:
res = cache.get(name)
if res is None: # Create the new instance
if value is None:
value = hash(name)
if cls in valid:
aux = cls
else:
aux = next(b for b in cls.mro() if b in valid)
if valid[aux](value):
res = super(symbol, cls).__new__(cls, value)
cache[name] = res
else:
msg = ('instancing "{}" with name "{}" and incorrect '
'value "{}" of type "{}"')
cn, vt = cls.__name__, type_name(value)
raise TypeError(msg.format(cn, name, value, vt))
elif res != value: # Check existing instance
msg = 'value "{}" mismatch for existing instance: "{}"'
raise ValueError(msg.format(value, name))
return res
else:
raise ValueError('name must be a valid non empty string')
def __init__(self, *args, **kwds):
pass
def __repr__(self):
return symbol.nameof(self)
__str__ = __repr__
[docs]class boolean(symbol):
'''Instances are custom logical values (`True` or `False`).
See `~MetaSymbol.__getitem__`:meth: operator for information on
constructor arguments.
For example::
>>> true = boolean('true', True)
>>> false = boolean('false')
>>> none = boolean('false')
>>> unset = boolean('unset')
>>> class X(object):
... attr = None
>>> getattr(X(), 'attr') is not None
False
>>> getattr(X(), 'attr', false) is not false
True
>>> none is false
True
>>> false == False
True
>>> false == unset
True
>>> false is unset
False
>>> true == True
True
'''
__slots__ = ()
def __new__(cls, name, value=False):
'''Get or create a new symbol instance.
See `~Symbol.__new__`:meth: for information about parameters.
'''
return super(boolean, cls).__new__(cls, name, bool(value))
# --- Special singleton values ---
#: False value, mainly for function parameter definitions, where None could
#: be a valid value.
Unset = boolean('Unset')
#: False value for local scope use or where ``Unset`` could be a valid value
Undefined = boolean('Undefined')
#: To be used in arguments that are currently ignored because they are being
#: deprecated. The only valid reason to use `Ignored` is to signal ignored
#: arguments in method's/function's signature
Ignored = boolean('Ignored')
#: To be used in functions resulting in a fail where False could be a valid
#: value.
Invalid = boolean('Invalid')
#: To be used as a mark for current context as a mechanism of comfort.
This = boolean('This', True)
del metaclass