Skip to content
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

Improve TimeRecurrence class #183

Merged
merged 9 commits into from
Oct 15, 2020
Merged

Conversation

MetRonnie
Copy link
Contributor

@MetRonnie MetRonnie commented Sep 10, 2020

Follow up to #165

  • Implements an __add__ & __sub__ methods in TimeRecurrence, allowing Durations to be added to TimeRecurrences. This takes the recurrence series and offsets it by the given duration.

  • Fixes a longstanding bug Fix the implementation of recurrence format number 1 #45 where the implementation of TimeRecurrence format no. 1 was incorrect. This format seems rarely used in Cylc so hopefully we can get away with this breaking change.

    The first recurrence format should not define repetitions in between the given start and end date - it actually should just extract the interval between the start and end dates and repeat that interval, starting at the start date


  • Includes tests
  • Changelog updated

@MetRonnie MetRonnie added this to the 2.1 milestone Sep 10, 2020
@MetRonnie MetRonnie self-assigned this Sep 10, 2020
oliver-sanders
oliver-sanders previously approved these changes Sep 11, 2020
Copy link
Member

@oliver-sanders oliver-sanders left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks ok, a quick test would be nice. (Outdated)

@MetRonnie MetRonnie marked this pull request as draft September 11, 2020 11:02
@MetRonnie MetRonnie changed the title Implement _copy() method for TimeRecurrence Implement __add__/__sub__ methods for TimeRecurrence Sep 14, 2020
@MetRonnie MetRonnie changed the title Implement __add__/__sub__ methods for TimeRecurrence Improve TimeRecurrence class Sep 17, 2020
@MetRonnie MetRonnie marked this pull request as ready for review September 17, 2020 17:34
@MetRonnie MetRonnie added the bug label Sep 17, 2020
@MetRonnie
Copy link
Contributor Author

Codacy is complaining about

Expression (recurrence_fmt3) + (data.TimePoint(year=2049, month_of_year=2)) is assigned to nothing

in a test. Can we change pylint to flake8 on there, and if so, how?

@MetRonnie MetRonnie requested a review from wxtim September 17, 2020 17:38
@MetRonnie MetRonnie linked an issue Sep 17, 2020 that may be closed by this pull request
@wxtim
Copy link
Contributor

wxtim commented Sep 18, 2020

Can we change pylint to flake8 on there, and if so, how?

I can't see a setup file so I think that you need to get a product owner on Codacy to change things there. I wouldn't worry too much about codacy failures.

I've proposed a change of the testing to use flake8, on the grounds of consistency with other projects at #185

Copy link
Contributor

@wxtim wxtim left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Essential

  • works
  • tests test what they should test

Non-essential

  • tests could look nicer. TBH I think there is a case for an entirely separate PR to refactor the tests for isodatetime into a pytest format. Don't worry about it here if you don't want to.

repetitions=rep, duration=dur,
end_point=data.TimePoint(year=2020, month_of_year=4,
day_of_month=3))
assert recurrence_fmt3 == recurrence_fmt4
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are you asserting equality here, when this is test for adding?
Is it to establish equality before you operate on these recurrences?
Should you also be asserting that recurrence_fmt3 == recurrence_fmt1

Suggested change
assert recurrence_fmt3 == recurrence_fmt4
# check that all three recurrence formats are seen as equal.
assert recurrence_fmt1 == recurrence_fmt3 == recurrence_fmt4

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I figured as they are meant to be the same it would help to check that in case something breaks (although there is a separate test for the TimeRecurrence comparison operators, at least it should let the developer know it's not a problem with the add/sub methods).

Format 1 might have been missed out because I hadn't yet fixed that format when writing the test, not sure

expected = data.TimeRecurrence(
repetitions=rep, start_point=new_start_pt, duration=dur)
for recurrence in (recurrence_fmt1, recurrence_fmt3, recurrence_fmt4):
assert recurrence + offset == expected
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels to me like it would be a lot cleaner parameterized. Sorry - hobby horse.

for recurrence in (recurrence_fmt1, recurrence_fmt3, recurrence_fmt4):
assert recurrence - offset == expected

with pytest.raises(TypeError):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO you should not be testing for success and failure in the same test as success.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

meh 😄

with pytest.raises(TypeError):
recurrence_fmt3 + data.TimePoint(year=2049, month_of_year=2)

def test_invalid_timerecurrence(self):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My view is that this would look nicer as

STUFF = {
        'start_pt': data.TimePoint(year=2020, day_of_month=9),
        'end_pt': data.TimePoint(year=2020, day_of_month=30),
        'dur': data.Duration(hours=36)
    }

@pytest.mark.parametrize(
    'kwargs',
    [
        {
            "repetitions": 4,
            "start_point": STUFF['start_pt'],
            "end_point": STUFF['end_pt'],
            "duration": STUFF['dur']
        },
        {
            "repetitions": None,
            "start_point": STUFF['start_pt'],
            "end_point": STUFF['end_pt'],
            "duration": STUFF['dur']
        },
    ]
)
def test_invalid_timerecurrence(kwargs):
    """Test that init'ing a TimeRecurrence with bad inputs raises error."""
    with pytest.raises(BadInputError):
        data.TimeRecurrence(**kwargs)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just tried that but it seems you can't parametrize methods of a class that extends unittest.TestCase. At some point we'll overhaul these tests... at some point... But I imagine it makes sense to leave the tests grouped together until such a time

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not too fussed, however, easy enough to dedent and remove the self argument.

Copy link
Member

@oliver-sanders oliver-sanders left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks ok to me:

  • Could do with a couple of simple str tests since that logic has changed slightly.
  • Couple of docs changes may require a little thought.

The old behaviour was quite nice for arbitrary "chunking":

R5/2000/2001  # split 2000 into five chunks

So there is a risk it may be in use somewhere. I've done a scan on our site and can't find any active uses so hopefully we are safe on that one. It would be a good idea to come up with an alternative (even if it uses Jinja2) we can document somewhere [in Cylc].

Reminder (to myself or whoever updates isodatetime in Cylc) to run though the recurrence docs (which could do with some TLC), check examples and document this.

Comment on lines +163 to +166
repetitions (int): The number of repetitions in the recurrence. If set
to 1, the recurrence simply consists of one date-time, with no
duration (i.e., the number of repetitions is inclusive of the first
occurrence). If omitted, the number of repetitions is unbounded.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO:

Check that R/2000 and R1/2000 still do the same thing.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't R/2000 invalid, as that means it's unbounded, but then no duration is specified so it's impossible to make a sequence out of it?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, this was a note to myself. Just checked R/2000 and R1/2000 still do the same thing they did before.

Copy link
Member

@oliver-sanders oliver-sanders left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good to go here I think.

Running the cylc test battery against this branch I get the same number of failures as for the master branch which is hopefully a good sign that this is unlikely to have broken anything.

@oliver-sanders oliver-sanders merged commit 0a0f20c into metomi:master Oct 15, 2020
@MetRonnie MetRonnie deleted the hashable-data2 branch October 15, 2020 14:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Fix the implementation of recurrence format number 1
3 participants