Skip to content

Commit 7467bc5

Browse files
committed
Added UserFloat to bases.
1 parent e5083f8 commit 7467bc5

File tree

2 files changed

+347
-4
lines changed

2 files changed

+347
-4
lines changed

domdf_python_tools/bases.py

Lines changed: 164 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232

3333
# stdlib
3434
from abc import abstractmethod
35+
from numbers import Real
3536
from pprint import pformat
3637
from typing import (
3738
Any,
@@ -41,6 +42,8 @@
4142
List,
4243
MutableSequence,
4344
Optional,
45+
SupportsFloat,
46+
SupportsRound,
4447
Tuple,
4548
Type,
4649
TypeVar,
@@ -50,11 +53,12 @@
5053

5154
# 3rd party
5255
import pydash # type: ignore
56+
from typing_extensions import Protocol
5357

5458
# this package
5559
from domdf_python_tools.doctools import prettify_docstrings
5660

57-
__all__ = ["Dictable", "NamedList", "namedlist", "UserList"]
61+
__all__ = ["Dictable", "NamedList", "namedlist", "UserList", "UserFloat"]
5862

5963
_V = TypeVar("_V")
6064

@@ -353,6 +357,165 @@ def extend(self, other: Iterable[_T]) -> None:
353357
self.data.extend(other)
354358

355359

360+
class _SupportsIndex(Protocol):
361+
362+
def __index__(self) -> int:
363+
...
364+
365+
366+
_F = TypeVar("_F", bound="UserFloat")
367+
368+
369+
@prettify_docstrings
370+
class UserFloat(Real, SupportsRound):
371+
"""
372+
Class that simulates a float.
373+
374+
:param value: Values to initialise the :class:`~domdf_python_tools.bases.UserFloat` with.
375+
376+
.. versionadded:: 1.6.0
377+
"""
378+
379+
def __init__(self, value: Union[SupportsFloat, _SupportsIndex, str, bytes, bytearray] = 0.0):
380+
self._value = (float(value), )
381+
382+
def as_integer_ratio(self) -> Tuple[int, int]:
383+
return float(self).as_integer_ratio()
384+
385+
def hex(self) -> str:
386+
return float(self).hex()
387+
388+
def is_integer(self) -> bool:
389+
return float(self).is_integer()
390+
391+
@classmethod
392+
def fromhex(cls: Type[_F], __s: str) -> _F:
393+
return cls(float.fromhex(__s))
394+
395+
def __add__(self: _F, other: float) -> _F:
396+
return self.__class__(float(self).__add__(other))
397+
398+
def __sub__(self: _F, other: float) -> _F:
399+
return self.__class__(float(self).__sub__(other))
400+
401+
def __mul__(self: _F, other: float) -> _F:
402+
return self.__class__(float(self).__mul__(other))
403+
404+
def __floordiv__(self: _F, other: float) -> _F: # type: ignore
405+
return self.__class__(float(self).__floordiv__(other))
406+
407+
def __truediv__(self: _F, other: float) -> _F:
408+
return self.__class__(float(self).__truediv__(other))
409+
410+
def __mod__(self: _F, other: float) -> _F:
411+
return self.__class__(float(self).__mod__(other))
412+
413+
def __divmod__(self: _F, other: float) -> Tuple[_F, _F]:
414+
return tuple(self.__class__(x) for x in float(self).__divmod__(other)) # type: ignore
415+
416+
def __pow__(self: _F, other: float, mod=None) -> _F:
417+
return self.__class__(float(self).__pow__(other, mod))
418+
419+
def __radd__(self: _F, other: float) -> _F:
420+
return self.__class__(float(self).__radd__(other))
421+
422+
def __rsub__(self: _F, other: float) -> _F:
423+
return self.__class__(float(self).__rsub__(other))
424+
425+
def __rmul__(self: _F, other: float) -> _F:
426+
return self.__class__(float(self).__rmul__(other))
427+
428+
def __rfloordiv__(self: _F, other: float) -> _F: # type: ignore
429+
return self.__class__(float(self).__rfloordiv__(other))
430+
431+
def __rtruediv__(self: _F, other: float) -> _F:
432+
return self.__class__(float(self).__rtruediv__(other))
433+
434+
def __rmod__(self: _F, other: float) -> _F:
435+
return self.__class__(float(self).__rmod__(other))
436+
437+
def __rdivmod__(self: _F, other: float) -> Tuple[_F, _F]:
438+
return tuple(self.__class__(x) for x in float(self).__rdivmod__(other)) # type: ignore
439+
440+
def __rpow__(self: _F, other: float, mod=None) -> _F:
441+
return self.__class__(float(self).__rpow__(other, mod))
442+
443+
def __getnewargs__(self) -> Tuple[float]:
444+
return self._value
445+
446+
def __trunc__(self) -> int:
447+
return float(self).__trunc__()
448+
449+
def __round__(self, ndigits: Optional[int] = None) -> Union[int, float]: # type: ignore
450+
return float(self).__round__(ndigits)
451+
452+
def __eq__(self, other: object) -> bool:
453+
if isinstance(other, UserFloat):
454+
return self._value == other._value
455+
else:
456+
return float(self).__eq__(other)
457+
458+
def __ne__(self, other: object) -> bool:
459+
if isinstance(other, UserFloat):
460+
return self._value != other._value
461+
else:
462+
return float(self).__ne__(other)
463+
464+
def __lt__(self, other: Union[float, "UserFloat"]) -> bool:
465+
if isinstance(other, UserFloat):
466+
return self._value < other._value
467+
else:
468+
return float(self).__lt__(other)
469+
470+
def __le__(self, other: Union[float, "UserFloat"]) -> bool:
471+
if isinstance(other, UserFloat):
472+
return self._value <= other._value
473+
else:
474+
return float(self).__le__(other)
475+
476+
def __gt__(self, other: Union[float, "UserFloat"]) -> bool:
477+
if isinstance(other, UserFloat):
478+
return self._value > other._value
479+
else:
480+
return float(self).__gt__(other)
481+
482+
def __ge__(self, other: Union[float, "UserFloat"]) -> bool:
483+
if isinstance(other, UserFloat):
484+
return self._value >= other._value
485+
else:
486+
return float(self).__ge__(other)
487+
488+
def __neg__(self: _F) -> _F:
489+
return self.__class__(float(self).__neg__())
490+
491+
def __pos__(self: _F) -> _F:
492+
return self.__class__(float(self).__pos__())
493+
494+
def __str__(self) -> str:
495+
return str(float(self))
496+
497+
def __int__(self) -> int:
498+
return int(float(self))
499+
500+
def __float__(self) -> float:
501+
return self._value[0]
502+
503+
def __abs__(self: _F) -> _F:
504+
return self.__class__(float(self).__abs__())
505+
506+
def __hash__(self) -> int:
507+
return float(self).__hash__()
508+
509+
def __repr__(self) -> str:
510+
return str(self)
511+
512+
def __ceil__(self):
513+
raise NotImplementedError
514+
515+
def __floor__(self):
516+
raise NotImplementedError
517+
518+
356519
@prettify_docstrings
357520
class NamedList(UserList[_T]):
358521
"""

tests/test_bases.py

Lines changed: 183 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,13 @@
99
# stdlib
1010
import copy
1111
import pickle # nosec: B101
12-
import sys
12+
from numbers import Number, Real
1313

1414
# 3rd party
1515
import pytest
1616

1717
# this package
18-
from domdf_python_tools.bases import Dictable, NamedList, UserList, namedlist
19-
from domdf_python_tools.utils import printr, printt
18+
from domdf_python_tools.bases import Dictable, UserFloat
2019

2120

2221
class Person(Dictable):
@@ -94,3 +93,184 @@ def test_subclass(self):
9493
child = Child("Bob", 12, "Big School")
9594
assert person == child
9695
assert "School" not in person.__dict__
96+
97+
98+
seven = UserFloat(7)
99+
100+
101+
class TestUserFloat:
102+
103+
def test_creation(self):
104+
assert isinstance(seven, Real)
105+
assert isinstance(seven, Number)
106+
107+
assert seven == 7
108+
assert seven == 7.0
109+
110+
def test_as_integer_ratio(self):
111+
assert seven.as_integer_ratio() == (7, 1)
112+
assert seven.as_integer_ratio() == 7.0.as_integer_ratio()
113+
114+
def test_hex(self):
115+
assert seven.hex() == "0x1.c000000000000p+2"
116+
assert seven.hex() == 7.0.hex()
117+
118+
def test_is_integer(self):
119+
assert seven.is_integer()
120+
assert seven.is_integer() == 7.0.is_integer()
121+
122+
def test_add(self):
123+
assert isinstance(seven + 7, UserFloat)
124+
assert seven + 7 == UserFloat(14)
125+
assert seven + 7 == 14
126+
assert seven + 7 == 14.0
127+
128+
def test_radd(self):
129+
assert isinstance(7 + seven, UserFloat)
130+
assert 7 + seven == UserFloat(14)
131+
assert 7 + seven == 14
132+
assert 7 + seven == 14.0
133+
134+
def test_sub(self):
135+
assert isinstance(seven - 3, UserFloat)
136+
assert seven - 3 == UserFloat(4)
137+
assert seven - 3 == 4
138+
assert seven - 3 == 4.0
139+
140+
def test_rsub(self):
141+
assert isinstance(3 - seven, UserFloat)
142+
assert 3 - seven == UserFloat(-4)
143+
assert 3 - seven == -UserFloat(4)
144+
assert 3 - seven == -4
145+
assert 3 - seven == -4.0
146+
147+
def test_mul(self):
148+
assert isinstance(seven * 3, UserFloat)
149+
assert seven * 3 == UserFloat(21)
150+
assert seven * 3 == 21
151+
assert seven * 3 == 21.0
152+
153+
def test_rmul(self):
154+
assert isinstance(3 * seven, UserFloat)
155+
assert 3 * seven == UserFloat(21)
156+
assert 3 * seven == UserFloat(21)
157+
assert 3 * seven == 21
158+
assert 3 * seven == 21.0
159+
160+
def test_div(self):
161+
assert isinstance(seven / 3, UserFloat)
162+
assert seven / 3 == UserFloat(7 / 3)
163+
assert seven / 3 == 7 / 3
164+
165+
def test_rdiv(self):
166+
assert isinstance(3 / seven, UserFloat)
167+
assert 3 / seven == UserFloat(3 / 7)
168+
assert 3 / seven == UserFloat(3 / 7)
169+
assert 3 / seven == 3 / 7
170+
171+
def test_floordiv(self):
172+
assert isinstance(seven // 3, UserFloat)
173+
assert seven // 3 == UserFloat(2)
174+
assert seven // 3 == 2
175+
assert seven // 3 == 2.0
176+
177+
def test_rfloordiv(self):
178+
assert isinstance(21 // seven, UserFloat)
179+
assert 21 // seven == UserFloat(3)
180+
assert 21 // seven == 3
181+
182+
def test_mod(self):
183+
assert isinstance(seven % 3, UserFloat)
184+
assert seven % 3 == UserFloat(1)
185+
assert seven % 3 == 1.0
186+
assert seven % 3 == 1
187+
188+
def test_rmod(self):
189+
assert isinstance(20 % seven, UserFloat)
190+
assert 20 % seven == UserFloat(6)
191+
assert 20 % seven == 6.0
192+
assert 20 % seven == 6
193+
194+
def test_pow(self):
195+
assert isinstance(seven**3, UserFloat)
196+
assert seven**3 == UserFloat(343)
197+
assert seven**3 == 343.0
198+
assert seven**3 == 343
199+
200+
def test_rpow(self):
201+
assert isinstance(3**seven, UserFloat)
202+
assert 3**seven == UserFloat(2187)
203+
assert 3**seven == 2187.0
204+
assert 3**seven == 2187
205+
206+
def test_round(self):
207+
assert isinstance(round(seven), int)
208+
assert round(seven) == 7
209+
assert isinstance(round(UserFloat(7.5)), int)
210+
assert round(UserFloat(7.5)) == 8
211+
212+
def test_repr_str_int(self):
213+
assert repr(seven) == "7.0"
214+
assert str(seven) == "7.0"
215+
assert int(seven) == 7
216+
assert isinstance(int(seven), int)
217+
218+
def test_lt(self):
219+
assert seven < 8
220+
assert seven < 8.0
221+
assert seven < UserFloat(8)
222+
223+
def test_le(self):
224+
assert seven <= 8
225+
assert seven <= 8.0
226+
assert seven <= UserFloat(8)
227+
assert seven <= 7
228+
assert seven <= 7.0
229+
assert seven <= UserFloat(7)
230+
231+
def test_gt(self):
232+
assert seven > 6
233+
assert seven > 6.0
234+
assert seven > UserFloat(6)
235+
236+
def test_ge(self):
237+
assert seven >= 6
238+
assert seven >= 6.0
239+
assert seven >= UserFloat(6)
240+
assert seven >= 7
241+
assert seven >= 7.0
242+
assert seven >= UserFloat(7)
243+
244+
def test_pos(self):
245+
assert isinstance(+seven, UserFloat)
246+
assert +seven == seven
247+
assert +seven == 7
248+
assert +seven == 7.0
249+
250+
def test_neg(self):
251+
assert isinstance(-seven, UserFloat)
252+
assert -seven == UserFloat(-7)
253+
assert -seven == -7
254+
assert -seven == -7.0
255+
256+
def test_abs(self):
257+
assert isinstance(abs(+seven), UserFloat)
258+
assert abs(+seven) == seven
259+
assert abs(+seven) == 7
260+
assert abs(+seven) == 7.0
261+
262+
assert isinstance(abs(-seven), UserFloat)
263+
assert abs(-seven) == UserFloat(7)
264+
assert abs(-seven) == 7
265+
assert abs(-seven) == 7.0
266+
267+
def test_ne(self):
268+
assert seven != UserFloat(8)
269+
assert seven != 8
270+
assert seven != 8.0
271+
272+
def test_hash(self):
273+
assert hash(seven) == hash(UserFloat(7))
274+
assert hash(seven) != hash(UserFloat(8))
275+
assert hash(seven) == hash(7)
276+
assert hash(seven) != hash(8)

0 commit comments

Comments
 (0)