-
-
Notifications
You must be signed in to change notification settings - Fork 16
/
conftest.py
144 lines (111 loc) · 4.42 KB
/
conftest.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
import pytest
import os
import icalendar
import sys
import time
try:
import zoneinfo as _zoneinfo
except ImportError:
try:
import backports.zoneinfo as _zoneinfo
except ImportError:
_zoneinfo = None
from x_wr_timezone import CalendarWalker
HERE = os.path.dirname(__file__)
REPO = os.path.dirname(HERE)
sys.path.append(REPO)
from recurring_ical_events import of
CALENDARS_FOLDER = os.path.join(HERE, "calendars")
# set the default time zone
# see https://stackoverflow.com/questions/1301493/setting-timezone-in-python
time.tzset()
class ICSCalendars:
"""A collection of parsed ICS calendars"""
Calendar = icalendar.Calendar
def get_calendar(self, content):
"""Return the calendar given the content."""
return self.Calendar.from_ical(content)
def __getitem__(self, name):
return getattr(self, name.replace("-", "_"))
@property
def raw(self):
return ICSCalendars()
def consistent_tz(self, dt):
"""Make the datetime consistent with the time zones used in these calendars."""
assert dt.tzinfo is None or "pytz" in dt.tzinfo.__class__.__module__, "We need pytz time zones for now."
return dt
for calendar_file in os.listdir(CALENDARS_FOLDER):
calendar_path = os.path.join(CALENDARS_FOLDER, calendar_file)
name = os.path.splitext(calendar_file)[0]
with open(calendar_path, "rb") as file:
content = file.read()
@property
def get_calendar(self, content=content):
return self.get_calendar(content)
attribute_name = name.replace("-", "_")
setattr(ICSCalendars, attribute_name, get_calendar)
class Calendars(ICSCalendars):
"""Collection of calendars from recurring_ical_events"""
def get_calendar(self, content):
return of(ICSCalendars.get_calendar(self, content))
class ReversedCalendars(ICSCalendars):
"""All test should run in reversed item order.
RFC5545:
This memo imposes no ordering of properties within an iCalendar object.
"""
def get_calendar(self, content):
"""Calendar traversing events in reversed order."""
calendar = ICSCalendars.get_calendar(self, content)
_walk = calendar.walk
def walk(*args, **kw):
"""Return properties in reversed order."""
return reversed(_walk(*args, **kw))
calendar.walk = walk
return of(calendar)
class ZoneInfoConverter(CalendarWalker):
"""Visit a calendar and change all time zones to ZoneInfo"""
def walk_value_datetime(self, dt):
"""Chnage timezone of datetime to zoneinfo"""
py_tz = dt.tzinfo
if py_tz is None:
return dt
name = py_tz.zone #, py_tz.tzname(dt)
new_tz = _zoneinfo.ZoneInfo(name) # create a zoneinfo from pytz time zone
return dt.astimezone(new_tz)
class ZoneInfoCalendars(ICSCalendars):
"""Collection of calendars using zoneinfo.ZoneInfo and not pytz."""
def __init__(self):
assert _zoneinfo is not None, "zoneinfo must exist to use these calendars"
self.changer = ZoneInfoConverter()
def get_calendar(self, content):
calendar = ICSCalendars.get_calendar(self, content)
zoneinfo_calendar = self.changer.walk(calendar)
# if zoneinfo_calendar is calendar:
# pytest.skip("ZoneInfo not in use. Already tested..")
return of(zoneinfo_calendar)
def consistent_tz(self, dt):
"""To make the time zones consistent with this one, convert them to zoneinfo."""
return self.changer.walk_value_datetime(dt)
calendar_params = [Calendars, ReversedCalendars]
if _zoneinfo is not None:
calendar_params.append(ZoneInfoCalendars)
# for parametrizing fixtures, see https://docs.pytest.org/en/latest/fixture.html#parametrizing-fixtures
@pytest.fixture(params=calendar_params, scope="module")
def calendars(request):
return request.param()
@pytest.fixture()
def todo():
"""Skip a test because it needs to be written first."""
pytest.skip("This test is not yet implemented.")
@pytest.fixture(scope='module')
def zoneinfo():
"""Return the zoneinfo module if present, otherwise skip the test.
Uses backports.zoneinfo or zoneinfo.
"""
if _zoneinfo is None:
pytest.skip("zoneinfo module not given. Use pip install backports.zoneinfo to install it.")
return _zoneinfo
@pytest.fixture(scope='module')
def ZoneInfo(zoneinfo):
"""Shortcut for zoneinfo.ZoneInfo."""
return zoneinfo.ZoneInfo