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.
"""
# $Id: undef.py 22691 2015-07-27 22:46:23Z gfiedler $
# Copyright Algebraix Data Corporation 2015 - $Date: 2015-07-27 17:46:23 -0500 (Mon, 27 Jul 2015) $
#
# 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/>.
# --------------------------------------------------------------------------------------------------
[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'
[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()