From 44adab0e9e47e7ea274dd7ddbf531b50075fafcc Mon Sep 17 00:00:00 2001 From: Ryan Petrello Date: Mon, 12 Mar 2018 09:39:19 -0400 Subject: [PATCH] bump python-dateutil to latest this change provides support for numerous bug fixes, along with support for parsing TZINFO= from rrule strings related: https://github.com/ansible/ansible-tower/issues/823 related: https://github.com/dateutil/dateutil/issues/614 --- awx/main/models/schedules.py | 49 +++---------------- .../tests/functional/api/test_schedules.py | 1 + requirements/requirements.in | 1 + requirements/requirements.txt | 2 +- 4 files changed, 11 insertions(+), 42 deletions(-) diff --git a/awx/main/models/schedules.py b/awx/main/models/schedules.py index 011ad82ed3..8736647a65 100644 --- a/awx/main/models/schedules.py +++ b/awx/main/models/schedules.py @@ -1,11 +1,10 @@ # Copyright (c) 2015 Ansible, Inc. # All Rights Reserved. -import re import logging import datetime import dateutil.rrule -from dateutil.tz import gettz, datetime_exists +from dateutil.tz import datetime_exists # Django from django.db import models @@ -57,10 +56,6 @@ class ScheduleManager(ScheduleFilterMethods, models.Manager): class Schedule(CommonModel, LaunchTimeConfig): - TZID_REGEX = re.compile( - "^(DTSTART;TZID=(?P[^:]+)(?P\:[0-9]+T[0-9]+))(?P .*)$" - ) - class Meta: app_label = 'main' ordering = ['-next_run'] @@ -103,51 +98,23 @@ class Schedule(CommonModel, LaunchTimeConfig): def rrulestr(cls, rrule, **kwargs): """ Apply our own custom rrule parsing logic to support TZID= - - python-dateutil doesn't _natively_ support `DTSTART;TZID=`; this - function parses out the TZID= component and uses it to produce the - `tzinfos` keyword argument to `dateutil.rrule.rrulestr()`. In this - way, we translate: - - DTSTART;TZID=America/New_York:20180601T120000 RRULE:FREQ=DAILY;INTERVAL=1 - - ...into... - - DTSTART:20180601T120000TZID RRULE:FREQ=DAILY;INTERVAL=1 - - ...and we pass a hint about the local timezone to dateutil's parser: - `dateutil.rrule.rrulestr(rrule, { - 'tzinfos': { - 'TZID': dateutil.tz.gettz('America/New_York') - } - })` - - it's likely that we can remove the custom code that performs this - parsing if TZID= gains support in upstream dateutil: - https://github.com/dateutil/dateutil/pull/619 """ kwargs['forceset'] = True - kwargs['tzinfos'] = {x: dateutil.tz.tzutc() for x in dateutil.parser.parserinfo().UTCZONE} - match = cls.TZID_REGEX.match(rrule) - if match is not None: - rrule = cls.TZID_REGEX.sub("DTSTART\gTZI\g", rrule) - timezone = gettz(match.group('tzid')) - kwargs['tzinfos']['TZI'] = timezone x = dateutil.rrule.rrulestr(rrule, **kwargs) for r in x._rrule: + if r._dtstart and r._dtstart.tzinfo is None: + raise ValueError( + 'A valid TZID must be provided (e.g., America/New_York)' + ) + if r._dtstart and r._until: + # If https://github.com/dateutil/dateutil/pull/634 ever makes + # it into a python-dateutil release, we could remove this block. if all(( r._dtstart.tzinfo != dateutil.tz.tzlocal(), r._until.tzinfo != dateutil.tz.tzutc(), )): - # According to RFC5545 Section 3.3.10: - # https://tools.ietf.org/html/rfc5545#section-3.3.10 - # - # > If the "DTSTART" property is specified as a date with UTC - # > time or a date with local time and time zone reference, - # > then the UNTIL rule part MUST be specified as a date with - # > UTC time. raise ValueError('RRULE UNTIL values must be specified in UTC') if 'MINUTELY' in rrule or 'HOURLY' in rrule: diff --git a/awx/main/tests/functional/api/test_schedules.py b/awx/main/tests/functional/api/test_schedules.py index a5e4d94c91..56c35d5f94 100644 --- a/awx/main/tests/functional/api/test_schedules.py +++ b/awx/main/tests/functional/api/test_schedules.py @@ -65,6 +65,7 @@ def test_valid_survey_answer(post, admin_user, project, inventory, survey_spec_f ("DTSTART:20300308T050000Z RRULE:FREQ=YEARLY;INTERVAL=1;BYYEARDAY=100", "BYYEARDAY not supported"), # noqa ("DTSTART:20300308T050000Z RRULE:FREQ=YEARLY;INTERVAL=1;BYWEEKNO=20", "BYWEEKNO not supported"), ("DTSTART:20300308T050000Z RRULE:FREQ=DAILY;INTERVAL=1;COUNT=2000", "COUNT > 999 is unsupported"), # noqa + ("DTSTART;TZID=US-Eastern:19961105T090000 RRULE:FREQ=MINUTELY;INTERVAL=10;COUNT=5", "A valid TZID must be provided"), # noqa ("DTSTART:20300308T050000Z RRULE:FREQ=REGULARLY;INTERVAL=1", "rrule parsing failed validation: invalid 'FREQ': REGULARLY"), # noqa ("DTSTART:20030925T104941Z RRULE:FREQ=DAILY;INTERVAL=10;COUNT=500;UNTIL=20040925T104941Z", "RRULE may not contain both COUNT and UNTIL"), # noqa ("DTSTART;TZID=America/New_York:20300308T050000Z RRULE:FREQ=DAILY;INTERVAL=1", "rrule parsing failed validation"), diff --git a/requirements/requirements.in b/requirements/requirements.in index c1eef4613a..3d98d5c283 100644 --- a/requirements/requirements.in +++ b/requirements/requirements.in @@ -38,6 +38,7 @@ pycrypto==2.6.1 pygerduty==0.37.0 pyOpenSSL==17.5.0 pyparsing==2.2.0 +python-dateutil==2.7.0 # contains support for TZINFO= parsing python-logstash==0.4.6 python-memcached==1.59 python-radius==1.0 diff --git a/requirements/requirements.txt b/requirements/requirements.txt index 24152b6764..deddb919ce 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -182,7 +182,7 @@ pyjwt==1.6.0 # via adal, social-auth-core, twilio pyopenssl==17.5.0 pyparsing==2.2.0 pyrad==2.1 # via django-radius -python-dateutil==2.6.1 # via adal, azure-cosmosdb-table, azure-storage-common, botocore +python-dateutil==2.7.0 python-ldap==2.5.2 # via django-auth-ldap python-logstash==0.4.6 python-memcached==1.59