-
Notifications
You must be signed in to change notification settings - Fork 2
/
llic.py
131 lines (98 loc) · 3.41 KB
/
llic.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
"""
Low-Level iCalendar library.
"""
from __future__ import unicode_literals
import pytz
import six
__version__ = "0.0.5"
__version_info__ = tuple(int(n) for n in __version__.split("."))
DEFAULT_ICAL_LINE_LENGTH = 75
CRLF = b"\r\n"
CRLF_WRAP = b"\r\n "
NAME_VALUE_SEPARATOR = b":"
class BaseCalendarWriter(object):
def __init__(self, output, line_length=DEFAULT_ICAL_LINE_LENGTH):
self.output = output
self.line_length = line_length
self.line_position = 0
def write(self, octets):
assert self.line_position <= self.line_length
if isinstance(octets, six.text_type):
# Only support UTF-8 output for now
octets = octets.encode("utf-8")
assert isinstance(octets, six.binary_type)
octets_len = len(octets)
if octets_len + self.line_position <= self.line_length:
self.output.write(octets)
self.line_position += octets_len
else:
self.__wrap_write(octets)
def __wrap_write(self, octets):
out = self.output
while True:
write_count = self.line_length - self.line_position
out.write(octets[:write_count])
octets = octets[write_count:]
if octets:
self.endline(True)
else:
break
def endline(self, is_wrapping):
out = self.output
if is_wrapping:
out.write(CRLF_WRAP)
self.line_position = 1
else:
out.write(CRLF)
self.line_position = 0
def start_contentline(self, name):
self.write(name)
self.write(NAME_VALUE_SEPARATOR)
def value(self, value):
self.write(value)
def end_contentline(self):
self.endline(False)
class TypesCalendarWriterHelperMixin(object):
# The following range of chars cannot occur in iCalendar TEXT, so we
# just delete them.
text_delete_chars = b"".join(
six.int2byte(c) for c in range(0x0, 0x20)
if c != ord(b"\n") # Ignore \n as it's handled by escaping)
)
def as_text(self, text):
"""
Encode text as an iCalendar TEXT value.
"""
if isinstance(text, six.text_type):
text = text.encode("utf-8")
# TEXT must be escaped as follows:
# \\ encodes \, \N or \n encodes newline
# \; encodes ;, \, encodes ,
text = text.replace(b"\\", b"\\\\") # escape \
text = text.replace(b"\n", b"\\n")
text = text.replace(b";", b"\\;")
text = text.replace(b",", b"\\,")
text = text.translate(None, self.text_delete_chars)
return text
def as_datetime(self, dt):
"""
Encode a datetime object as an iCalendar DATETIME in UTC.
"""
if dt.tzinfo is None:
raise ValueError("dt must have a tzinfo, got: {!r}".format(dt))
if dt.tzinfo != pytz.utc:
dt = dt.astimezone(pytz.utc)
return dt.strftime("%Y%m%dT%H%M%SZ")
class CalendarWriterHelperMixin(object):
def contentline(self, name, value):
self.start_contentline(name)
self.value(value)
self.end_contentline()
def begin(self, section):
self.contentline("BEGIN", section)
def end(self, section):
self.contentline("END", section)
class CalendarWriter(TypesCalendarWriterHelperMixin,
CalendarWriterHelperMixin,
BaseCalendarWriter):
pass