-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
#2003 Change behavior of approx.py
to only support __eq__
comparison
#2576
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
#2003 Change behavior of approx.py
to only support __eq__
comparison
#2576
Conversation
approx.py
to only support __eq__
comparison
First of all thanks a ton for the PR! 👍 🎆 According to the documentation, we should return the
I tried your PR locally and it givens this error:
Changing to return
|
@nicoddemus thanks for having a look at it that fast. I should have read the whole part of the doc I guess :). I will submit another commit that returns a singleton. I guess I'll get to it the end of the weekend. Did you think about my comment, about raising a user warning? I mentioned it in this PR. Does that make sense and do you have any tip in doing so?
|
I don't think a warning is necessary given that now it will fail with a |
@nicoddemus I just had a brief look and it seems that just returning |
Yes, but we have to return |
So, I had some time to test this. I changed all ( Python 2 also calls Python 2.7.12 (default, Nov 19 2016, 06:48:10)
[GCC 5.4.0 20160609] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from pytest import approx
>>> 1 > approx(1, rel=1e-6, abs=1e-12)
False
>>> 1 < approx(1, rel=1e-6, abs=1e-12)
True def __gt__(self, actual):
return NotImplemented
def __lt__(self, actual):
return NotImplemented
def __cmp__(self, actual):
return NotImplemented |
@maiksensi that's weird,
I don't know how to proceed actually, perhaps we should look into why is Python calling Hmm just occurred to me, we shouldn't even implement |
It doesn't change the behavior. >>> a
1 +- 1.0e-06
>>> b
1
>>> type(b)
<type 'int'>
>>> b < a
True
>>> b > a
False
>>> pdb.runeval(b<a)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python2.7/pdb.py", line 1241, in runeval
return Pdb().runeval(expression, globals, locals)
File "/usr/lib/python2.7/bdb.py", line 416, in runeval
expr = expr+'\n'
TypeError: unsupported operand type(s) for +: 'bool' and 'str'
>>> pdb.runeval(b>a)
> /home/maiksen/repos/pytest/_pytest/python_api.py(38)__lt__()
-> return NotImplemented Is the interpreter maybe falling back to some sort of identity comparison? |
@maiksensi you need to put |
@RonnyPfannschmidt ah thanks! Obviously, didn't know that. Will do and see how it goes. |
So, giving it a shot the correct way: >>> pdb.runeval("b>a")
> <string>(1)<module>()->True
(Pdb) s
--Call--
> /home/maiksen/repos/pytest/_pytest/python_api.py(37)__lt__()
-> def __lt__(self, actual):
(Pdb) s
> /home/maiksen/repos/pytest/_pytest/python_api.py(38)__lt__()
-> return NotImplemented
(Pdb) s
--Return--
> /home/maiksen/repos/pytest/_pytest/python_api.py(38)__lt__()->NotImplemented
-> return NotImplemented
(Pdb) s
--Return--
> <string>(1)<module>()->False
(Pdb) s
> /usr/lib/python2.7/bdb.py(422)runeval()
-> self.quitting = 1
(Pdb) s
False
>>> pdb.runeval("b<a")
> <string>(1)<module>()->False
(Pdb) s
--Call--
> /home/maiksen/repos/pytest/_pytest/python_api.py(34)__gt__()
-> def __gt__(self, actual):
(Pdb) s
> /home/maiksen/repos/pytest/_pytest/python_api.py(35)__gt__()
-> return NotImplemented
(Pdb) s
--Return--
> /home/maiksen/repos/pytest/_pytest/python_api.py(35)__gt__()->NotImplemented
-> return NotImplemented
(Pdb) s
--Return--
> <string>(1)<module>()->True
(Pdb) s
> /usr/lib/python2.7/bdb.py(422)runeval()
-> self.quitting = 1
(Pdb) s
True
>>> and with python 3.6 getting the expected >>> pdb.runeval("b<a")
> <string>(1)<module>()->None
(Pdb) s
--Call--
> /home/maiksen/repos/pytest/_pytest/python_api.py(180)__gt__()
-> def __gt__(self, actual):
(Pdb) s
> /home/maiksen/repos/pytest/_pytest/python_api.py(181)__gt__()
-> return NotImplemented
(Pdb) s
--Return--
> /home/maiksen/repos/pytest/_pytest/python_api.py(181)__gt__()->NotImplemented
-> return NotImplemented
(Pdb) s
TypeError: '<' not supported between instances of 'int' and 'ApproxScalar'
> <string>(1)<module>()->None
(Pdb) s
--Return--
> <string>(1)<module>()->None
(Pdb) s
TypeError: '<' not supported between instances of 'int' and 'ApproxScalar'
... |
Unfortunately just defining the rich-comparison operators in Python 2 is not enough it seems. I went ahead and raised a # content of test_foo.py
import pytest
def test_aprox_inequal_le():
assert pytest.approx(0.0) <= 0.0
def test_aprox_inequal_l():
assert pytest.approx(0.0) < 0.0
def test_aprox_inequal_ge():
assert pytest.approx(0.0) >= 0.0
def test_aprox_inequal_g():
assert pytest.approx(0.0) > 0.0
def test_equal():
assert pytest.approx(0.0) == 0.0 Python 2:
Python 3:
The message is different because we can't know which operator triggered Here's my patch (based on this branch): diff --git a/_pytest/python_api.py b/_pytest/python_api.py
index d605add..ba6ea5a 100644
--- a/_pytest/python_api.py
+++ b/_pytest/python_api.py
@@ -31,15 +31,14 @@ class ApproxBase(object):
__hash__ = None
- def __gt__(self, actual):
- raise NotImplementedError
-
- def __lt__(self, actual):
- raise NotImplementedError
-
def __ne__(self, actual):
return not (actual == self)
+ if sys.version_info.major == 2:
+ def __cmp__(self, other):
+ __tracebackhide__ = True
+ raise TypeError('Comparison operators other than == and != not supported by approx objects')
+
def _approx_scalar(self, x):
return ApproxScalar(x, rel=self.rel, abs=self.abs, nan_ok=self.nan_ok)
@@ -112,11 +111,10 @@ class ApproxNumpyBase(ApproxBase):
return "approx({0!r})".format(list(
self._approx_scalar(x) for x in self.expected))
- def __gt__(self, actual):
- raise NotImplementedError
-
- def __lt__(self, actual):
- raise NotImplementedError
+ if sys.version_info.major == 2:
+ def __cmp__(self, other):
+ __tracebackhide__ = True
+ raise TypeError('Comparison operators other than == and != not supported by approx objects')
def __eq__(self, actual):
import numpy as np What do you guys think? |
I would have proposed to manually raise a |
I think I still have this change locally at home. If you are OK with it, I can push it to your branch then. |
Sure!
…On Jul 25, 2017 5:18 PM, "Bruno Oliveira" ***@***.***> wrote:
I think I still have this change locally at home. If you are OK with it, I
can push it to your branch then.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#2576 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AGEJWuNbUXU7ocG_0_yxNAGyMwfjCFS_ks5sRgcxgaJpZM4OZA-z>
.
|
The new doc section explains why we raise a `NotImplementedError`.
d8eface
to
a152cfe
Compare
Applied the changes and rebased on |
a152cfe
to
80f4699
Compare
This PR changes the current behavior of
approx
to raiseNotImplementedError
for all comparisons but__eq__
as discussed in #2003Note: I thought about adding a warn that this is currently not supported like:
warnings.warn("Currently not supported") raise NotImplementedError
But we found #2452 mentioning that
pytest.config.warn
should be deprecated. Also, I was not sure how to use this in approx, as it is not a fixture and theconfig
object is not available out of the box. Can we just use thewarnings
module there? Or is mentioning this in the docs enough?