-
-
Notifications
You must be signed in to change notification settings - Fork 18.4k
Implement missing offset comparison methods #18738
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
Conversation
assert isinstance(delta, Timedelta) | ||
assert delta < offset | ||
assert delta <= offset | ||
assert not delta == offset |
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.
these are not correct comparisons, eg.. not a == b
is equiv to a != b
and the linter complains about the former.
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 the linting is complaining I can change it to explicitly call e.g not a.__ne__(b)
. The methods have distinct implementations, need distinct tests.
with pytest.raises(TypeError): | ||
off < 3 | ||
|
||
# Unfortunately there is no good way to make the reverse inequality work |
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.
huh?
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.
We can make Tick.__richmp__(timedelta)
work, but we can't make timedelta.__richcmp__(Tick)
work. AFAICT datetime
for deferring to other
but timedelta
does not.
if isinstance(other, DateOffset): | ||
return f(self, other) | ||
else: | ||
if op == operator.eq: |
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 are you repeating
isn't
return op(self, other)
enough 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.
You mean in line 2181 returning f(self, other)
instead of op(self, other)
? Because the the behavior still depends on which DateOffset subclass other
is.
other=other.__class__.__name__)) | ||
elif op == operator.eq: | ||
# TODO: Consider changing this older behavior for | ||
# __eq__ and __ne__to match other comparisons |
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 is this about?
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 the status quo __eq__
returns False
(and __ne__
returns True
) as the fallback when other
cannot be converted to a DateOffset
. The other comparisons raise in this situation. (I think Timedelta behaves the same way, so this might be OK). This is just a note to double-check that we __eq__
and __ne__
should continue to be treated as special cases.
if most of the way there, then let's take it over the line. |
#8386 can be split into two parts. First is unimplemented comparison methods, which this PR addresses as many of as I could find. Second is that author implicitly expects |
Codecov Report
@@ Coverage Diff @@
## master #18738 +/- ##
==========================================
- Coverage 91.67% 91.66% -0.01%
==========================================
Files 154 154
Lines 51368 51388 +20
==========================================
+ Hits 47092 47106 +14
- Misses 4276 4282 +6
Continue to review full report at Codecov.
|
@jbrockmendel , I'm OK with this fix. Many thanks for the PR!! |
off <= Day(n=7) | ||
|
||
|
||
def test_comparison_names(offset_types): |
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.
can you parametrize
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.
add a whatsnew note for #8386 see if anything listed there which should be a tests, if not lmk that.
@@ -3147,3 +3147,52 @@ def test_require_integers(offset_types): | |||
cls = offset_types | |||
with pytest.raises(ValueError): | |||
cls(n=1.5) | |||
|
|||
|
|||
def test_comparisons(offset_types): |
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 this needs to be broken up a bit, its very hard to read / determine what is allowed and what is not.
IOW you need 3 tests
Week, Tick, Everything else
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 the Tick test should be in tests_ticks (and you have one already there)
off < DateOffset(month=7) | ||
|
||
|
||
def test_week_comparison(): |
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 you already have week (yes this is constructed differently), but pls group these together.
|
||
def cmp_func(self, other): | ||
if type(self) == Week and self.weekday is None: | ||
# Week without weekday behaves like a Tick |
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.
blank line
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.
Sure, but for clarification, I thought that wasn't necessary when the pre-comment line has different indentation. Is there a reference for this I can bookmark?
# Week without weekday behaves like a Tick | ||
tick = Day(n=7 * self.n, normalize=self.normalize) | ||
return op(tick, other) | ||
else: |
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.
no need for the else
other_delta = other.delta | ||
elif isinstance(other, (timedelta, np.timedelta64)): | ||
other_delta = other | ||
elif isinstance(other, Week) and other.weekday is None: |
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 happens when other.weekday is not None?
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 would lump all Week in this case (use if statements inside if needed)
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 happens when other.weekday is not None?
TypeError
I would lump all Week in this case (use if statements inside if needed)
Leads to a bunch of duplicated code if we do it that way.
Handling array-like comparisons correctly is going to take some additional work. Temporarily closing to clear the queue. |
git diff upstream/master -u -- "*.py" | flake8 --diff
Not sure if it closes #8386, but its most of the way there.
Fix some missing comparisons, including Timedelta with Tick and Tick with (timedelta|Timedelta|Week)
Attach correct names for comparison methods.
If we can resolve #18510, we can make
Week(weekday=None)
just return aTick
, avoid some special casing.