#!/usr/bin/env python
# -*- encoding: utf-8 -*-
# ---------------------------------------------------------------------
# infinity
# ---------------------------------------------------------------------
# Copyright (c) 2016, 2017 Merchise Autrement and Contributors
# All rights reserved.
#
# This is free software; you can redistribute it and/or modify it under the
# terms of the LICENCE attached (see LICENCE file) in the distribution
# package.
#
# Created on 2016-07-04
'''The Infinity value.
Not all values are comparable with `Infinity`:obj: by default. The ABC
`InfinityComparable`:class: holds the registry of such values. Any `number
<numbers.Number>`:class: is comparable with `Infinity`:obj:.
`Dates <datetime.date>`:class:, `datetimes <datetime.datetime>`:class: and
`time deltas <datetime.timedelta>`:class: are also registered by default.
.. warning:: In Python 2, dates, datetimes and time deltas must be the right
operand, as in ``Infinity > today``. Doing ``today < Infinity``
fails in Python 2.
'''
from __future__ import (division as _py3_division,
print_function as _py3_print,
absolute_import as _py3_abs_import)
import abc
import datetime
from numbers import Number
from functools import total_ordering
from xoutil.eight.meta import metaclass
[docs]class InfinityComparable(metaclass(abc.ABCMeta)):
'''Any type that can be sensibly compared to infinity.
All types in the `number <numbers.Number>`:class: tower are *always*
comparable.
Classes `datetime.date`:class:, `datetime.datetime`:class:, and
`datetime.timedelta`:class: are automatically registered.
'''
@classmethod
def __subclasshook__(self, cls):
if isinstance(cls, type) and issubclass(cls, Number):
return True
else:
return NotImplemented
InfinityComparable.register(datetime.date)
InfinityComparable.register(datetime.datetime)
InfinityComparable.register(datetime.timedelta)
@total_ordering
class InfinityType(object):
_positive = None
_negative = None
def __new__(cls, sign):
if sign < 0:
res = cls._negative
if not res:
cls._negative = res = object.__new__(cls)
else:
res = cls._positive
if not res:
cls._positive = res = object.__new__(cls)
return res
def __init__(self, sign):
self.sign = -1 if sign < 0 else 1
def __lt__(self, other):
if isinstance(other, InfinityComparable):
return self.sign < 0 # True iff -Infinity
elif isinstance(other, InfinityType):
return self.sign < other.sign
else:
raise TypeError(
'Incomparable types: %r and %r' % (type(self), type(other))
)
def __eq__(self, other):
if isinstance(other, InfinityType):
return self.sign == other.sign
else:
return False
def __neg__(self):
return type(self)(-self.sign)
def __str__(self):
return '∞' if self.sign > 0 else '-∞'
def __repr__(self):
return 'Infinity' if self.sign > 0 else '-Infinity'
Infinity = InfinityType(+1)