Source code for algebraixlib.undef

r"""Facilities for representing and working with the concept of "undefined".

Most operations are not defined for all types of data: :term:`set` operations may not be defined on
:term:`couplet`\s, :term:`multiset` operations may not be defined on sets, and so on. When an
operation is not defined for a given input, it returns the singleton `Undef()`. This return value
can then be taken into account by the caller. In some cases it is an error, in other cases the
result is simply ignored.
"""

# Copyright Algebraix Data Corporation 2015 - 2017
#
# This file is part of algebraixlib <http://github.com/AlgebraixData/algebraixlib>.
#
# algebraixlib is free software: you can redistribute it and/or modify it under the terms of version
# 3 of the GNU Lesser General Public License as published by the Free Software Foundation.
#
# algebraixlib is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License along with algebraixlib.
# If not, see <http://www.gnu.org/licenses/>.
# --------------------------------------------------------------------------------------------------
from algebraixlib.tmp_sqlda_op import tmp_sqlda_op
from .cache_status import CacheStatus


[docs]class Undef: """A singleton class that represents the concept of "undefined". Instances of this class are not treated as a value by the operations in this library; specifically, it will never appear as the value of an :class:`~.Atom`. """ _instance = None
[docs] def __new__(cls): """Override ``__new__`` to create a singleton class.""" if Undef._instance is None: Undef._instance = super().__new__(cls) return Undef._instance
[docs] def __eq__(self, other): """Prevent comparisons on Undef; raise a TypeError.""" raise TypeError("== is not supported by Undef")
[docs] def __ne__(self, other): """Prevent comparisons on Undef; raise a TypeError.""" raise TypeError("!= is not supported by Undef")
def __bool__(self): """Prevent comparisons on Undef; raise a TypeError.""" raise TypeError("Boolean conversion is not supported by Undef")
[docs] def __repr__(self): """Return the instance's code representation.""" return 'Undef()'
[docs] def __str__(self): """Return the instance's string representation.""" return 'undef'
# ---------------------------------------------------------------------------------------------- # Property cache functions. # Indicate MathObject type (2-state binary logic). @property def is_atom(self) -> bool: """Return ``False`` since :class:`~.Undef` is not an :class:`~.Atom`.""" return False @property def is_couplet(self) -> bool: """Return ``False`` since :class:`~.Undef` is not a :class:`~.Couplet`.""" return False @property def is_multiset(self) -> bool: """Return ``False`` since :class:`~.Undef` is not a :class:`~.Multiset`.""" return False @property def is_set(self) -> bool: """Return ``False`` since :class:`~.Undef` is not a :class:`~.Set`.""" return False # Indicate algebra membership. @property def cached_relation(self) -> int: """Return the cached state of being a :term:`relation`. See [PropCache]_.""" return CacheStatus.IS_NOT @property def cached_is_relation(self) -> bool: """Return ``False`` since ``self`` is known not to be a :term:`relation`. See [PropCache]_.""" return False @property def cached_is_not_relation(self) -> bool: """Return ``True`` since ``self`` is known not to be a :term:`relation`. See [PropCache]_.""" return True @property def cached_clan(self) -> int: """Return the cached state of being a :term:`clan`. See [PropCache]_.""" return CacheStatus.IS_NOT @property def cached_is_clan(self) -> bool: """Return ``False`` since ``self`` is known not to be a :term:`clan`. See [PropCache]_.""" return False @property def cached_is_not_clan(self) -> bool: """Return ``True`` since ``self`` is known not to be a :term:`clan`. See [PropCache]_.""" return True @property def cached_multiclan(self) -> int: """Return the cached state of being a :term:`multiclan`. See [PropCache]_.""" return CacheStatus.IS_NOT @property def cached_is_multiclan(self) -> bool: """Return ``False`` since ``self`` is known to not be a :term:`multiclan`. See [PropCache]_.""" return False @property def cached_is_not_multiclan(self) -> bool: """Return ``True`` since ``self`` is known not to be a :term:`multiclan`. See [PropCache]_.""" return True @property def cached_absolute(self) -> int: """Return the cached state of being :term:`absolute`. See [PropCache]_. .. note:: Keep in mind that this does not tell you what kind of absolute algebra member this is. For example, an absolute :term:`relation` is a non-absolute :term:`set`. """ return CacheStatus.IS_NOT @property def cached_is_absolute(self) -> bool: """Return ``False`` since ``self`` is known not to be :term:`absolute`. See [PropCache]_. .. note:: Keep in mind that this does not tell you what kind of absolute algebra member this is known to be. For example, an absolute :term:`relation` is a non-absolute :term:`set`. """ return False @property def cached_is_not_absolute(self) -> bool: """Return ``True`` since ``self`` is known not to be :term:`absolute`. See [PropCache]_. .. note:: Keep in mind that this does not tell you what kind of absolute algebra member this is known not to be. For example, an absolute :term:`relation` is a non-absolute :term:`set`. """ return True # Relation properties (defined on relations, clans, multiclans). @property def cached_functional(self) -> int: """Return the cached state of being :term:`functional`. See [PropCache]_.""" return CacheStatus.N_A @property def cached_is_functional(self) -> bool: """Return ``False`` since :term:`functional` does not apply. See [PropCache]_.""" return False @property def cached_is_not_functional(self) -> bool: """Return ``False`` since :term:`functional` does not apply. See [PropCache]_.""" return False @property def cached_right_functional(self) -> int: """Return the cached state of being :term:`right-functional`. See [PropCache]_.""" return CacheStatus.N_A @property def cached_is_right_functional(self) -> bool: """Return ``False`` since :term:`right-functional` does not apply. See [PropCache]_.""" return False @property def cached_is_not_right_functional(self) -> bool: """Return ``False`` since :term:`right-functional` does not apply. See [PropCache]_.""" return False @property def cached_reflexive(self) -> int: """Return the cached state of being :term:`reflexive`. See [PropCache]_.""" return CacheStatus.N_A @property def cached_is_reflexive(self) -> bool: """Return ``False`` since :term:`reflexive` does not apply. See [PropCache]_.""" return False @property def cached_is_not_reflexive(self) -> bool: """Return ``False`` since :term:`reflexive` does not apply. See [PropCache]_.""" return False @property def cached_symmetric(self) -> int: """Return the cached state of being :term:`symmetric`. See [PropCache]_.""" return CacheStatus.N_A @property def cached_is_symmetric(self) -> bool: """Return ``False`` since :term:`symmetric` does not apply. See [PropCache]_.""" return False @property def cached_is_not_symmetric(self) -> bool: """Return ``False`` since :term:`symmetric` does not apply. See [PropCache]_.""" return False @property def cached_transitive(self) -> int: """Return the cached state of being :term:`transitive`. See [PropCache]_.""" return CacheStatus.N_A @property def cached_is_transitive(self) -> bool: """Return ``False`` since :term:`transitive` does not apply. See [PropCache]_.""" return False @property def cached_is_not_transitive(self) -> bool: """Return ``False`` since :term:`transitive` does not apply. See [PropCache]_.""" return False # Clan properties (defined on clans, multiclans). @property def cached_regular(self) -> int: """Return the cached state of being :term:`regular`. See [PropCache]_.""" return CacheStatus.N_A @property def cached_is_regular(self) -> bool: """Return ``False`` since :term:`regular` does not apply. See [PropCache]_.""" return False @property def cached_is_not_regular(self) -> bool: """Return ``False`` since :term:`regular` does not apply. See [PropCache]_.""" return False @property def cached_right_regular(self) -> int: """Return the cached state of being :term:`right-regular`. See [PropCache]_.""" return CacheStatus.N_A @property def cached_is_right_regular(self) -> bool: """Return ``False`` since :term:`right-regular` does not apply. See [PropCache]_.""" return False @property def cached_is_not_right_regular(self) -> bool: """Return ``False`` since :term:`right-regular` does not apply. See [PropCache]_.""" return False
[docs]class RaiseOnUndef: """Manage the level for `make_or_raise_undef`. Implemented as static class.""" #: The 'normal' level. _reset_level = 0 #: The current level. _level = _reset_level def __init__(self): raise AssertionError("Don't instantiate RaiseOnUndef class. Use it as a static class only.") @staticmethod
[docs] def get_level(): """Return the current level for raising an `UndefException`. The exception is raised if the ``level`` argument of `make_or_raise_undef` is less than or equal to the value returned here. """ return RaiseOnUndef._level
@staticmethod
[docs] def set_level(temp_value): """Set the level for raising an `UndefException` temporarily to ``temp_value``.""" RaiseOnUndef._level = temp_value
@staticmethod
[docs] def reset(): """Reset the level for raising an `UndefException` back to its initial value.""" RaiseOnUndef._level = RaiseOnUndef._reset_level
[docs]class UndefException(Exception): """This exception is raised when the ``level`` argument of `make_or_raise_undef` is less than or equal to the `RaiseOnUndef` level.""" pass
[docs]def make_or_raise_undef(level=1): """Raise `UndefException` if ``level`` is less than or equal to the `RaiseOnUndef` level, otherwise return `Undef()`. :param level: An integer >= 1. Default is 1. .. note:: Use 1 (or no argument) for the cases that are most likely to be errors (like wrong argument types). Use higher numbers for cases that may return `Undef()` on purpose. """ if level <= RaiseOnUndef.get_level(): raise UndefException("Result is undefined. See also 'undef.RaiseOnUndef'.") return Undef()
[docs]def make_or_raise_undef2(obj): """Raise `UndefException` if ``level`` is less than or equal to the `RaiseOnUndef` level, otherwise return `Undef()`. :param obj: Causes ``level`` argument to `make_or_raise_undef` to be 2 if `Undef()` """ if obj is Undef(): return make_or_raise_undef(2) return make_or_raise_undef()
@tmp_sqlda_op(True)
[docs]def make_undef(): """Return `Undef()`. Used where a hashable instance that evaluates to `Undef()` is needed.""" return Undef()