-
-
Notifications
You must be signed in to change notification settings - Fork 18.3k
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
TST: restructure internal extension arrays tests (split between /arrays and /extension) #22026
TST: restructure internal extension arrays tests (split between /arrays and /extension) #22026
Conversation
pandas/tests/arrays/test_integer.py
Outdated
assert 'length' in result | ||
|
||
|
||
class TestConstructors(BaseInteger): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is still some dependence on the base extension tests here, in the fact that we use the overridden self.assert_series_equal
etc.
However, I think that tm.assert_series_equal
should be able to correctly handle our internal EAs, and this should not be necessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And apparently the tm.
methods already work, so not sure why they were overridden in original test_integer.py
(@jreback do you remember?)
pandas/pandas/tests/extension/integer/test_integer.py
Lines 92 to 110 in a439472
def assert_index_equal(self, left, right, *args, **kwargs): | |
left_na = left.isna() | |
right_na = right.isna() | |
tm.assert_numpy_array_equal(left_na, right_na) | |
return tm.assert_index_equal(left[~left_na], | |
right[~right_na], | |
*args, **kwargs) | |
def assert_series_equal(self, left, right, *args, **kwargs): | |
left_na = left.isna() | |
right_na = right.isna() | |
tm.assert_series_equal(left_na, right_na) | |
return tm.assert_series_equal(left[~left_na], | |
right[~right_na], | |
*args, **kwargs) |
pandas/tests/arrays/test_integer.py
Outdated
self.assert_series_equal(result, expected) | ||
|
||
|
||
class TestArithmeticOps(BaseOpsUtil, BaseInteger): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BaseOpsUtil
here is only used for get_op_from_name
. So probably makes sense to simply copy that, or have somewhere a utility function for that.
result = result.astype(float) | ||
elif op_name.startswith('__r'): | ||
# TODO reverse operators result in object dtype | ||
# see https://github.com/pandas-dev/pandas/issues/22024 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
some of the added checks here have to do with certain 'buggy' (or unexpected) behaviours (see the issues I opened)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if you are doing a PR to move things, don't change code. I have no idea what you are changing here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, the original code is still in tests/arrays/test_integer.py
, so I only moved it there.
The changes here are simply to get the base class tests working instead of overwriting them as you did (and again, those custom tests are still in the other file).
General note: I didn't really think about how to structure the new |
+1 to your proposed restructure. |
Codecov Report
@@ Coverage Diff @@
## master #22026 +/- ##
=======================================
Coverage 92.04% 92.04%
=======================================
Files 169 169
Lines 50782 50782
=======================================
Hits 46742 46742
Misses 4040 4040
Continue to review full report at Codecov.
|
@jreback @jbrockmendel @jschendel any comments? |
I think this is terribly confusing where do so yes something is in 2 places I would much be all internal arrays in there entirely to pandas/tests/arrays and leave the base classes and inherited machinery in pandas/tests/extension |
So you mean leaving the actual test base classes in |
I would simply move test_integer, test_interval, and test_categorical in there entirety. All the tests are then in 1 place. It is immateterial that a test is inherited or not. In fact I didnt' respond to your comments above, but that IS the reason I purposely write the tests in the way I did. When debugging you want to change a single line and not even think / worry about any testing artifices (e.g. the fact that most tests are automatically run on your behalf and thus inherited). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
see comments
Sorry, Jeff, I don't understand your comment. Can you try to clarify it?
What does this mean? You mean that you don't find it relevant or confusing that tests in
You mean the fact that
Can you give a concrete example or clarify this? |
To be clear: my proposal is to keep only the most basic tests as possible in |
well that’s the issue i couldn’t find an obvious split which makes it clear of what belongs where |
Well, I propose here a possible, IMO relatively clear, split |
Agreed. When I'm working on the interface, I want to be able to run it against all the test and public EAs, so I'd like to run |
this is not clear at all |
Interface test under extensions.
Other tests under arrays.
…________________________________
From: Jeff Reback <notifications@github.com>
Sent: Wednesday, July 25, 2018 6:23:55 AM
To: pandas-dev/pandas
Cc: Tom Augspurger; Comment
Subject: Re: [pandas-dev/pandas] TST: restructure internal extension arrays tests (split between /arrays and /extension) (#22026)
this is not clear at all
i don’t think it adds value
rather it makes the question of where to put things come up
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub<#22026 (comment)>, or mute the thread<https://github.com/notifications/unsubscribe-auth/ABQHIkGa4t9zZ6i4DTSbajxHWk5PZlF1ks5uKFVLgaJpZM4VbJ4J>.
|
The current organisation is for sure not clear as well. For example, when actually doing the reorganisation in this PR, I had to look at lot which tests were in the base classes that were or were not overridden, which made it very complicated to work with those tests. If, eg in the TestArithmeticOps of the integer array tests, you need to override almost everything from the base class, there is something wrong (and in fact, it was also not needed as I show in this PR). Also, for the integer array, we are going to add a lot more tests while we develop it further (proper reduction, groupby, overflow handling, ...) that all will have a lot of integer-array specificities. Further mixing tests for this with Base test classes that are not meant for such specificities, will further complicate this IMO. As Tom summarized, the proposed split: in the extension tests we only run the existing interface tests (the base classes) and only do the minimum to get those passing for eg IntegerArray (see this PR), everything else that is specific about integer arrays is tested in So if you really think that this is "terribly confusing", then please try to give an alternative proposal. |
@jorisvandenbossche its up to you to give a new proposal. I think this confusing. If I am confused, think about the average contributor. |
He has, with this PR! Are you -1 here? Otherwise I don't think it's worth trying to explain again. |
yes I am -1 here. I think the existing test structure is fine. |
I am bringing it up as an example of the functionaility split. To be honest its VERY VERY hard to test what the actual custom type vs specific test is doing. This is my point. We need to split by functionaility FIRST. and ONLY. This custom type / interface splitting is very confusing. |
so I am done for today with this. Merge what you want. |
Again, as said multiple times above, we already have the same situation currently in master, where you also need to decide whether to put something in the BaseExtension classes or as a new test function/method in the same file. If you keep saying that it is not obvious how to split them, can you then at least answer to this?
Euhm, in master all tests are agglomareated into a single file |
Jeff, to come back to splitting up by functionality (#22026 (comment)). In practice (over time), we will not have
but of course something like:
So we will split by functionality for the custom tests. We only propose to keep the basic interface tests together in a single file (not all the other tests), as they are simply inheriting from the BaseExtension tests. |
Added a commit that should hopefully fix the tests. One part of it splitted off as its own bug fix: #22441 It would be good if some people could review the actual code changes. |
Quickly discussed this on hangout:
I will also still some documentation / comments as discussed above (#22026 (comment)) |
I added an explanation of the scope of the tests to each of |
super(TestArithmeticOps, self).check_opname(s, op_name, | ||
other, exc=None) | ||
|
||
def _check_op(self, s, op, other, op_name, exc=NotImplementedError): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here (below), I added quite a bit of hoops to get the tests passing. This is partly because it is not easy to have generic arithmetic tests (certainly in case of integers where we different subdtypes), and I could also simply have skipped this class completely (since we still test arithmetic operations in a more custom way in tests/arrays/test_integer.py
). But since trying to get those passing, actually turned up some bugs / corner cases (see the issue links in the comments below), I think it is useful to keep this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should change this to have individual checkers (kind of like we have _check_divmod_op
) which are defined in the base class, then it is straightforward to just override these here (and its not as complicated code)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That might be a good idea, but note that we would still need to do some of the below hoops for each of the operations, so not sure if it would turn out simpler.
Also, since this would touch all tests of all types and is more a refactor of the base extension tests, I would like to leave that for a separate PR. Here, I only try to split the existing integer and interval tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
k, can you create an issue to track / refactor this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I already opened a bunch of issues for the actual bugs (see links below). For the reworking of the tests, I don't have a concrete idea how it would look like now, so I would rather not open a vague issue (but feel free to open one if you have a concrete idea of how to refactor it)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
some comments. approach is ok.
pandas/tests/arrays/test_integer.py
Outdated
@@ -14,6 +13,8 @@ | |||
Int8Dtype, Int16Dtype, Int32Dtype, Int64Dtype, | |||
UInt8Dtype, UInt16Dtype, UInt32Dtype, UInt64Dtype) | |||
|
|||
from ..extension.base import BaseOpsUtil |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we are generally using absolute imports
@@ -69,7 +69,8 @@ def test_arith_series_with_array(self, data, all_arithmetic_operators): | |||
# ndarray & other series | |||
op_name = all_arithmetic_operators | |||
s = pd.Series(data) | |||
self.check_opname(s, op_name, [s.iloc[0]] * len(s), exc=TypeError) | |||
self.check_opname(s, op_name, pd.Series([s.iloc[0]] * len(s)), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why this change?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similar as what Tom said below: combine
doesn't work with lists (it might be we should improve combine
, but that is a separate issue)
|
||
class BaseInteger(object): | ||
|
||
def assert_index_equal(self, left, right, *args, **kwargs): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in general are these necessary for extensions? shouldn't we just incorporate these directly into tm.assert_frame_equal / tm.assert_series_equal
? (e.g. this was changed in tests/arrays/test_integer.py`` why not here & generally)?
IOW except for line 99 (the tm.assert_numpy_array_equal, which is related to the impl), all of this is completely generic (and just series and ops on them)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For now I simply split it, and left the logic of this file as it was before (and indeed in tests/arrays/test_integer.py
I no longer used it because I needed to remove the dependency on the base classes there, and it also turned out not to be needed).
Do you remember why you added this in the first place?
I agree that in general this should not be needed (it is good to have this mechanism to overwrite it for external EAs, but you would expect assert_series_equal
to work just fine for our own arrays).
Interval and categorical also don't have this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Quickly commented out this part, and all tests are still passing. So will remove and check if all passes on travis as well
super(TestArithmeticOps, self).check_opname(s, op_name, | ||
other, exc=None) | ||
|
||
def _check_op(self, s, op, other, op_name, exc=NotImplementedError): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should change this to have individual checkers (kind of like we have _check_divmod_op
) which are defined in the base class, then it is straightforward to just override these here (and its not as complicated code)
super(TestArithmeticOps, self)._check_divmod_op(s, op, other, None) | ||
|
||
@pytest.mark.skip(reason="intNA does not error on ops") | ||
def test_error(self, data, all_arithmetic_operators): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
was this moved? or are you just showing that this 'works'?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, it's a bit a strange test. I think @TomAugspurger changed that in the sparse PR (or commented about it)
Hello @jorisvandenbossche! Thanks for updating the PR.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
generally looks fine. some minor comments.
orig_data1._from_sequence([a + b for (a, b) in | ||
zip(list(orig_data1), | ||
list(orig_data2))])) | ||
with np.errstate(over='ignore'): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what surfaces the error here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When testing with Int8 dtype, you get an overflow warning for summing the scalars (the a + b
below)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm ok, maybe we should actually move this to the integer tests rather than here then?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it is fine to ignore it here (it's numpy behaviour to warn, not something we need to test), but it's true that we might want to add explicit test for the behaviour we want on the Series level in the integer tests.
The behaviour currently to silently ignore it in the case of series is consistent with a Series with numpy dtype int8:
In [38]: s = pd.Series([50, 100], dtype='Int8')
In [39]: s[0] + s[1]
/home/joris/miniconda3/envs/dev/bin/ipython:1: RuntimeWarning: overflow encountered in byte_scalars
#!/home/joris/miniconda3/envs/dev/bin/python
Out[39]: -106
In [40]: s + s[::-1].reset_index(drop=True)
Out[40]:
0 -106
1 -106
dtype: Int8
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i understand though this may be indicative of an error in other extension types. ok for now
|
||
@pytest.fixture | ||
def data(dtype): | ||
return integer_array(make_data(), dtype=dtype) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@TomAugspurger should create an issue to document fixtures in the extension array tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think also should have these fixtures in a conftest.py as NotImplementedError (IOW if an author doesn't implement the fixtures they should raise loudly). Once you implement they override and will work.
super(TestArithmeticOps, self).check_opname(s, op_name, | ||
other, exc=None) | ||
|
||
def _check_op(self, s, op, other, op_name, exc=NotImplementedError): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
k, can you create an issue to track / refactor this.
rebase on master as well. |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Everything is passing now! Merging |
…ys and /extension) (pandas-dev#22026)
…ys and /extension) (pandas-dev#22026)
Pull request to discuss what to do with the tests for internal EAs (and one of the comments I still had in #21160)
Basically, I would keep the
tests/extension/..
only for subclassing the base extension array test suite, and any array-specific functionality is tested intests/arrays/..
(eg closed attribute for IntervalArray, specific arithmetic behaviour for IntegerArray, ...)This means that when adding a test related to EAs, we need to think about: is this testing something that is applicable to all EAs? (-> add a base test to
tests/extension/base
so this is tested for all internal and external EAs) or is this testing something specific to a particular EA? (-> add a test intests/array/EAtype/..
)Of course often there can be some ambiguity here.
Main reason that I would split them is that over time, we probably add a lot of EA-type-specific tests, and then keeping the general ones mixed with the specific ones will make it only confusing / hard to see what is going on. Drawback is of course that it is tested in two places.
In practice what I propose in this PR, is also what we already do for Categorical at the moment: Categorical has its own tests in
tests/arrays/categorical
(and probably also some inindexes
andframe
, ..), but we also run the base extension tests for Categorical intests/extension/