Skip to content

Commit 0f707c6

Browse files
committed
Update isodatetime to 3.2
Fix bug where `next(...)` could return past datetime
1 parent 5b19013 commit 0f707c6

File tree

8 files changed

+50
-73
lines changed

8 files changed

+50
-73
lines changed

conda-environment.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ dependencies:
99
- graphviz # for static graphing
1010
# Note: can't pin jinja2 any higher than this until we give up on Cylc 7 back-compat
1111
- jinja2 >=3.0,<3.1
12-
- metomi-isodatetime >=1!3.0.0, <1!3.2.0
12+
- metomi-isodatetime >=1!3.2.0, <1!3.3.0
1313
- packaging
1414
# Constrain protobuf version for compatible Scheduler-UIS comms across hosts
1515
- protobuf >=4.24.4,<4.25.0

cylc/flow/commands.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -55,33 +55,33 @@
5555
from contextlib import suppress
5656
from time import sleep, time
5757
from typing import (
58+
TYPE_CHECKING,
5859
AsyncGenerator,
5960
Callable,
6061
Dict,
6162
Iterable,
6263
List,
6364
Optional,
64-
TYPE_CHECKING,
6565
Union,
6666
)
6767

68-
from cylc.flow import LOG
68+
from metomi.isodatetime.parsers import TimePointParser
69+
6970
import cylc.flow.command_validation as validate
71+
import cylc.flow.flags
72+
from cylc.flow import LOG
7073
from cylc.flow.exceptions import (
7174
CommandFailedError,
7275
CyclingError,
7376
CylcConfigError,
7477
)
75-
import cylc.flow.flags
7678
from cylc.flow.log_level import log_level_to_verbosity
7779
from cylc.flow.network.schema import WorkflowStopMode
7880
from cylc.flow.parsec.exceptions import ParsecError
7981
from cylc.flow.task_id import TaskID
80-
from cylc.flow.task_state import TASK_STATUSES_ACTIVE, TASK_STATUS_FAILED
82+
from cylc.flow.task_state import TASK_STATUS_FAILED, TASK_STATUSES_ACTIVE
8183
from cylc.flow.workflow_status import RunMode, StopMode
8284

83-
from metomi.isodatetime.parsers import TimePointParser
84-
8585
if TYPE_CHECKING:
8686
from cylc.flow.scheduler import Scheduler
8787

@@ -194,7 +194,7 @@ async def stop(
194194
# schedule shutdown after wallclock time passes provided time
195195
parser = TimePointParser()
196196
schd.set_stop_clock(
197-
int(parser.parse(clock_time).seconds_since_unix_epoch)
197+
parser.parse(clock_time).seconds_since_unix_epoch
198198
)
199199
schd._update_workflow_state()
200200
elif task is not None:

cylc/flow/cycling/iso8601.py

Lines changed: 1 addition & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
import re
2222
from typing import List, Optional, TYPE_CHECKING, Tuple
2323

24-
from metomi.isodatetime.data import Calendar, CALENDAR, Duration
24+
from metomi.isodatetime.data import Calendar, CALENDAR
2525
from metomi.isodatetime.dumpers import TimePointDumper
2626
from metomi.isodatetime.timezone import (
2727
get_local_time_zone, get_local_time_zone_format, TimeZoneFormatMode)
@@ -699,14 +699,6 @@ def ingest_time(value: str, now: Optional[str] = None) -> str:
699699
now = get_current_time_string()
700700
now_point = parser.parse(now)
701701

702-
# correct for year in 'now' if year is the only date unit specified -
703-
# https://github.com/cylc/cylc-flow/issues/4805#issuecomment-1103928604
704-
if re.search(r"\(-\d{2}[);T]", value):
705-
now_point += Duration(years=1)
706-
# likewise correct for month if year and month are the only date units
707-
elif re.search(r"\(-\d{4}[);T]", value):
708-
now_point += Duration(months=1)
709-
710702
# perform whatever transformation is required
711703
offset = None
712704
if is_prev_next:
@@ -799,28 +791,6 @@ def prev_next(
799791

800792
cycle_point = timepoints[my_diff.index(min(my_diff))]
801793

802-
# ensure truncated dates do not have time from 'now' included' -
803-
# https://github.com/metomi/isodatetime/issues/212
804-
if 'T' not in value.split(')')[0]:
805-
# NOTE: Strictly speaking we shouldn't forcefully mutate TimePoints
806-
# in this way as they're meant to be immutable since
807-
# https://github.com/metomi/isodatetime/pull/165, however it
808-
# should be ok as long as the TimePoint is not used as a dict key and
809-
# we don't call any of the TimePoint's cached methods until after we've
810-
# finished mutating it.
811-
cycle_point._hour_of_day = 0
812-
cycle_point._minute_of_hour = 0
813-
cycle_point._second_of_minute = 0
814-
# likewise ensure month and day from 'now' are not included
815-
# where they did not appear in the truncated datetime
816-
if re.search(r"\(-\d{2}[);T]", value):
817-
# case 1 - year only
818-
cycle_point._month_of_year = 1
819-
cycle_point._day_of_month = 1
820-
elif re.search(r"\(-(-\d{2}|\d{4})[;T)]", value):
821-
# case 2 - month only or year and month
822-
cycle_point._day_of_month = 1
823-
824794
return cycle_point, offset
825795

826796

cylc/flow/scheduler.py

Lines changed: 34 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,24 @@
1616
"""Cylc scheduler server."""
1717

1818
import asyncio
19+
import os
20+
import sys
21+
import traceback
1922
from collections import deque
2023
from contextlib import suppress
21-
import os
2224
from pathlib import Path
2325
from queue import Empty, Queue
2426
from shlex import quote
2527
from socket import gaierror
26-
from subprocess import DEVNULL, PIPE, Popen
27-
import sys
28+
from subprocess import (
29+
DEVNULL,
30+
PIPE,
31+
Popen,
32+
)
2833
from threading import Barrier, Thread
2934
from time import sleep, time
30-
import traceback
3135
from typing import (
36+
TYPE_CHECKING,
3237
Any,
3338
AsyncGenerator,
3439
Callable,
@@ -38,36 +43,36 @@
3843
NoReturn,
3944
Optional,
4045
Set,
41-
TYPE_CHECKING,
4246
Tuple,
4347
Union,
4448
)
4549
from uuid import uuid4
4650

4751
import psutil
4852

53+
import cylc.flow.flags
54+
from cylc.flow import LOG
55+
from cylc.flow import __version__ as CYLC_VERSION
4956
from cylc.flow import (
50-
LOG,
51-
__version__ as CYLC_VERSION,
57+
commands,
5258
main_loop,
59+
workflow_files,
5360
)
54-
from cylc.flow import workflow_files
5561
from cylc.flow.broadcast_mgr import BroadcastMgr
5662
from cylc.flow.cfgspec.glbl_cfg import glbl_cfg
5763
from cylc.flow.config import WorkflowConfig
58-
from cylc.flow import commands
5964
from cylc.flow.data_store_mgr import DataStoreMgr
6065
from cylc.flow.exceptions import (
6166
CommandFailedError,
6267
CylcError,
6368
InputError,
6469
)
65-
import cylc.flow.flags
66-
from cylc.flow.flow_mgr import FLOW_NEW, FLOW_NONE, FlowMgr
67-
from cylc.flow.host_select import (
68-
HostSelectException,
69-
select_workflow_host,
70+
from cylc.flow.flow_mgr import (
71+
FLOW_NEW,
72+
FLOW_NONE,
73+
FlowMgr,
7074
)
75+
from cylc.flow.host_select import HostSelectException, select_workflow_host
7176
from cylc.flow.hostuserutil import (
7277
get_host,
7378
get_user,
@@ -86,8 +91,8 @@
8691
from cylc.flow.network import API
8792
from cylc.flow.network.authentication import key_housekeeping
8893
from cylc.flow.network.server import WorkflowRuntimeServer
89-
from cylc.flow.parsec.OrderedDict import DictTree
9094
from cylc.flow.parsec.exceptions import ParsecError
95+
from cylc.flow.parsec.OrderedDict import DictTree
9196
from cylc.flow.parsec.validate import DurationFloat
9297
from cylc.flow.pathutil import (
9398
get_workflow_name_from_id,
@@ -121,34 +126,37 @@
121126
REMOTE_INIT_FAILED,
122127
)
123128
from cylc.flow.task_state import (
124-
TASK_STATUSES_ACTIVE,
125-
TASK_STATUSES_NEVER_ACTIVE,
126129
TASK_STATUS_PREPARING,
127130
TASK_STATUS_RUNNING,
128131
TASK_STATUS_SUBMITTED,
129132
TASK_STATUS_WAITING,
133+
TASK_STATUSES_ACTIVE,
134+
TASK_STATUSES_NEVER_ACTIVE,
130135
)
131136
from cylc.flow.taskdef import TaskDef
132-
from cylc.flow.templatevars import eval_var
133-
from cylc.flow.templatevars import get_template_vars
137+
from cylc.flow.templatevars import eval_var, get_template_vars
134138
from cylc.flow.timer import Timer
135139
from cylc.flow.util import cli_format
136-
from cylc.flow.wallclock import (
137-
get_current_time_string,
138-
get_time_string_from_unix_time as time2str,
139-
get_utc_mode,
140-
)
140+
from cylc.flow.wallclock import get_current_time_string
141+
from cylc.flow.wallclock import get_time_string_from_unix_time as time2str
142+
from cylc.flow.wallclock import get_utc_mode
141143
from cylc.flow.workflow_db_mgr import WorkflowDatabaseManager
142144
from cylc.flow.workflow_events import WorkflowEventHandler
143-
from cylc.flow.workflow_status import AutoRestartMode, RunMode, StopMode
145+
from cylc.flow.workflow_status import (
146+
AutoRestartMode,
147+
RunMode,
148+
StopMode,
149+
)
144150
from cylc.flow.xtrigger_mgr import XtriggerManager
145151

146152
if TYPE_CHECKING:
147153
# BACK COMPAT: typing_extensions.Literal
148154
# FROM: Python 3.7
149155
# TO: Python 3.8
150-
from typing_extensions import Literal
151156
from optparse import Values
157+
158+
from typing_extensions import Literal
159+
152160
from cylc.flow.network.resolvers import TaskMsg
153161

154162

cylc/flow/task_proxy.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -399,7 +399,7 @@ def get_point_as_seconds(self):
399399
"""Compute and store my cycle point as seconds since epoch."""
400400
if self.point_as_seconds is None:
401401
iso_timepoint = point_parse(str(self.point))
402-
self.point_as_seconds = int(iso_timepoint.seconds_since_unix_epoch)
402+
self.point_as_seconds = iso_timepoint.seconds_since_unix_epoch
403403
if iso_timepoint.time_zone.unknown:
404404
utc_offset_hours, utc_offset_minutes = (
405405
get_local_time_zone())
@@ -429,8 +429,7 @@ def get_clock_trigger_time(
429429
else:
430430
trigger_time = point + ISO8601Interval(offset_str)
431431

432-
offset = int(
433-
point_parse(str(trigger_time)).seconds_since_unix_epoch)
432+
offset = point_parse(str(trigger_time)).seconds_since_unix_epoch
434433
self.clock_trigger_times[offset_str] = offset
435434
return self.clock_trigger_times[offset_str]
436435

setup.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ install_requires =
6868
graphene>=2.1,<3
6969
# Note: can't pin jinja2 any higher than this until we give up on Cylc 7 back-compat
7070
jinja2==3.0.*
71-
metomi-isodatetime>=1!3.0.0,<1!3.2.0
71+
metomi-isodatetime>=1!3.2.0,<1!3.3.0
7272
# Constrain protobuf version for compatible Scheduler-UIS comms across hosts
7373
packaging
7474
protobuf>=4.24.4,<4.25.0

tests/unit/cycling/test_iso8601.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -685,7 +685,7 @@ def test_simple(set_cycling_type):
685685
('next(--0325)', '20110325T0000Z'),
686686
('next(---10)', '20100810T0000Z'),
687687
('next(---05T1200Z)', '20100905T1200Z'),
688-
param('next(--08-08)', '20110808T0000Z', marks=pytest.mark.xfail),
688+
('next(--08-08)', '20110808T0000Z'),
689689
('next(T15)', '20100809T1500Z'),
690690
('next(T-41)', '20100808T1541Z'),
691691
]
@@ -709,7 +709,7 @@ def test_next_simple(value: str, expected: str, set_cycling_type):
709709
('previous(--0325)', '20100325T0000Z'),
710710
('previous(---10)', '20100710T0000Z'),
711711
('previous(---05T1200Z)', '20100805T1200Z'),
712-
param('previous(--08-08)', '20100808T0000Z', marks=pytest.mark.xfail),
712+
('previous(--08-08)', '20100808T0000Z'),
713713
('previous(T15)', '20100808T1500Z'),
714714
('previous(T-41)', '20100808T1441Z'),
715715
]
@@ -856,7 +856,7 @@ def test_weeks_days(set_cycling_type):
856856
('previous(--1225)', '20171225T0000Z'),
857857
('next(-2006)', '20200601T0000Z'),
858858
('previous(-W101)', '20180305T0000Z'),
859-
('next(-W-1; -W-3; -W-5)', '20180314T0000Z'),
859+
('next(-W-1; -W-3; -W-5)', '20180316T0000Z'),
860860
('next(-001; -091; -181; -271)', '20180401T0000Z'),
861861
('previous(-365T12Z)', '20171231T1200Z'),
862862
]

tests/unit/test_workflow_status.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ def schd(
8989
WORKFLOW_STATUS_RUNNING_TO_STOP % 4
9090
),
9191
(
92-
{'stop_clock_time': int(STOP_TIME.seconds_since_unix_epoch)},
92+
{'stop_clock_time': STOP_TIME.seconds_since_unix_epoch},
9393
WorkflowStatus.RUNNING,
9494
WORKFLOW_STATUS_RUNNING_TO_STOP % str(STOP_TIME)
9595
),

0 commit comments

Comments
 (0)