Skip to content

Commit

Permalink
Merge branch 'master' into issue-27
Browse files Browse the repository at this point in the history
  • Loading branch information
jacadzaca committed Sep 30, 2023
2 parents 032a553 + 0fdbdfb commit ccc3810
Show file tree
Hide file tree
Showing 22 changed files with 506 additions and 39 deletions.
46 changes: 41 additions & 5 deletions CHANGES.rst
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
Changelog
=========

5.0.8 (unreleased)
------------------
5.0.11 (unreleased)
-------------------

Minor changes:

- Update build configuration to build readthedocs. #538
- No longer run the ``plone.app.event`` tests.
- Move pip caching into Python setup action.
- The cli utility now displays start and end datetimes in the user's local timezone.
Ref: #561
[vimpostor]

Breaking changes:

Expand All @@ -22,6 +22,42 @@ Bug fixes:

- ...

5.0.10 (unreleased)
-------------------

Bug fixes:

- Component._encode stops ignoring parameters argument on native values, now merges them
Fixes: #557
[zocker1999net]

5.0.9 (2023-09-24)
------------------

Bug fixes:

- PERIOD values now set the timezone of their start and end. #556

5.0.8 (2023-09-18)
------------------

Minor changes:

- Update build configuration to build readthedocs. #538
- No longer run the ``plone.app.event`` tests.
- Add documentation on how to parse ``.ics`` files. #152
- Move pip caching into Python setup action.
- Check that issue #165 can be closed.
- Updated about.rst for issue #527
- Avoid ``vText.__repr__`` BytesWarning.

Bug fixes:

- Calendar components are now properly compared
Ref: #550
Fixes: #526
[jacadzaca]

5.0.7 (2023-05-29)
------------------

Expand Down
21 changes: 21 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,27 @@ files.
.. _`pytz`: https://pypi.org/project/pytz/
.. _`BSD`: https://github.com/collective/icalendar/issues/2

Quick Guide
-----------

To **install** the package, run::

pip install icalendar

You can open an ``.ics`` file and see all the events::

>>> import icalendar
>>> path_to_ics_file = "src/icalendar/tests/calendars/example.ics"
>>> with open(path_to_ics_file) as f:
... calendar = icalendar.Calendar.from_ical(f.read())
>>> for event in calendar.walk('VEVENT'):
... print(event.get("SUMMARY"))
New Year's Day
Orthodox Christmas
International Women's Day

Using this package, you can also create calendars from scratch or edit existing ones.

Versions and Compatibility
--------------------------

Expand Down
6 changes: 2 additions & 4 deletions docs/about.rst
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
About
=====

`Max M`_ had often needed to parse and generate iCalendar files. Finally he got
`Max M`_ had often needed to parse and generate iCalendar files. Finally, he got
tired of writing ad-hoc tools. This package is his attempt at making an
iCalendar package for Python. The inspiration has come from the email package
in the standard lib, which he thinks is pretty simple, yet efficient and
powerful.

At the time of writing this, last version was released more then 2 years ago.
Since then many things have changes. For one, `RFC 2445`_ was updated by `RFC
5545`_ which makes this package. So in some sense this package became outdated.
The icalendar package is an RFC 5545-compatible parser/generator for iCalendar files.

.. _`Max M`: http://www.mxm.dk
.. _`RFC 2445`: https://tools.ietf.org/html/rfc2445
Expand Down
2 changes: 2 additions & 0 deletions docs/credits.rst
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ icalendar contributors
- Thomas Bruederli <thomas@roundcube.net>
- Thomas Weißschuh <thomas@t-8ch.de>
- Victor Varvaryuk <victor.varvariuc@gmail.com>
- Ville Skyttä <ville.skytta@iki.fi>
- Wichert Akkerman <wichert@wiggy.net>
- cillianderoiste <cillian.deroiste@gmail.com>
- fitnr <fitnr@fakeisthenewreal>
Expand All @@ -69,6 +70,7 @@ icalendar contributors
- `Natasha Mattson <https://github.com/natashamm`_
- `NikEasY <https://github.com/NikEasY>`_
- Matt Lewis <git@semiprime.com>
- Felix Stupp <felix.stupp@banananet.work>

Find out who contributed::

Expand Down
2 changes: 1 addition & 1 deletion docs/install.rst
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ Try it out:
Type "help", "copyright", "credits" or "license" for more information.
>>> import icalendar
>>> icalendar.__version__
'5.0.7'
'5.0.10'
Building the documentation locally
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand Down
2 changes: 1 addition & 1 deletion docs/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -342,5 +342,5 @@ Print out the calendar::
More documentation
==================

Have a look at the tests of this package to get more examples.
Have a look at the `tests <https://github.com/collective/icalendar/tree/master/src/icalendar/tests>`__ of this package to get more examples.
All modules and classes docstrings, which document how they work.
2 changes: 1 addition & 1 deletion src/icalendar/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
__version__ = '5.0.7'
__version__ = '5.0.10'

from icalendar.cal import (
Calendar,
Expand Down
44 changes: 33 additions & 11 deletions src/icalendar/cal.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,8 @@ def is_broken(self):
#############################
# handling of property values

def _encode(self, name, value, parameters=None, encode=1):
@staticmethod
def _encode(name, value, parameters=None, encode=1):
"""Encode values to icalendar property values.
:param name: Name of the property.
Expand All @@ -138,17 +139,19 @@ def _encode(self, name, value, parameters=None, encode=1):
return value
if isinstance(value, types_factory.all_types):
# Don't encode already encoded values.
return value
klass = types_factory.for_property(name)
obj = klass(value)
obj = value
else:
klass = types_factory.for_property(name)
obj = klass(value)
if parameters:
if isinstance(parameters, dict):
params = Parameters()
for key, item in parameters.items():
params[key] = item
parameters = params
assert isinstance(parameters, Parameters)
obj.params = parameters
if not hasattr(obj, "params"):
obj.params = Parameters()
for key, item in parameters.items():
if item is None:
if key in obj.params:
del obj.params[key]
else:
obj.params[key] = item
return obj

def add(self, name, value, parameters=None, encode=1):
Expand Down Expand Up @@ -436,6 +439,25 @@ def __repr__(self):
subs = ', '.join(str(it) for it in self.subcomponents)
return f"{self.name or type(self).__name__}({dict(self)}{', ' + subs if subs else ''})"

def __eq__(self, other):
if not len(self.subcomponents) == len(other.subcomponents):
return False

properties_equal = super().__eq__(other)
if not properties_equal:
return False

# The subcomponents might not be in the same order,
# neither there's a natural key we can sort the subcomponents by nor
# are the subcomponent types hashable, so we cant put them in a set to
# check for set equivalence. We have to iterate over the subcomponents
# and look for each of them in the list.
for subcomponent in self.subcomponents:
if subcomponent not in other.subcomponents:
return False

return True


#######################################
# components defined in RFC 5545
Expand Down
4 changes: 2 additions & 2 deletions src/icalendar/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,10 @@ def view(event):
end = event.decoded('dtend', default=start)
duration = event.decoded('duration', default=end - start)
if isinstance(start, datetime):
start = start.astimezone(start.tzinfo)
start = start.astimezone()
start = start.strftime('%c')
if isinstance(end, datetime):
end = end.astimezone(end.tzinfo)
end = end.astimezone()
end = end.strftime('%c')

return f""" Organizer: {organizer}
Expand Down
10 changes: 5 additions & 5 deletions src/icalendar/prop.py
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,7 @@ def from_ical(cls, ical, timezone=None):
if u.startswith(('P', '-P', '+P')):
return vDuration.from_ical(ical)
if '/' in u:
return vPeriod.from_ical(ical)
return vPeriod.from_ical(ical, timezone=timezone)

if len(ical) in (15, 16):
return vDatetime.from_ical(ical, timezone=timezone)
Expand Down Expand Up @@ -548,11 +548,11 @@ def to_ical(self):
+ vDatetime(self.end).to_ical())

@staticmethod
def from_ical(ical):
def from_ical(ical, timezone=None):
try:
start, end_or_duration = ical.split('/')
start = vDDDTypes.from_ical(start)
end_or_duration = vDDDTypes.from_ical(end_or_duration)
start = vDDDTypes.from_ical(start, timezone=timezone)
end_or_duration = vDDDTypes.from_ical(end_or_duration, timezone=timezone)
return (start, end_or_duration)
except Exception:
raise ValueError(f'Expected period format, got: {ical}')
Expand Down Expand Up @@ -719,7 +719,7 @@ def __new__(cls, value, encoding=DEFAULT_ENCODING):
return self

def __repr__(self):
return f"vText('{self.to_ical()}')"
return f"vText('{self.to_ical()!r}')"

def to_ical(self):
return escape_char(self).encode(self.encoding)
Expand Down
40 changes: 40 additions & 0 deletions src/icalendar/tests/calendars/example.ics
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
BEGIN:VCALENDAR
VERSION:2.0
PRODID:collective/icalendar
CALSCALE:GREGORIAN
METHOD:PUBLISH
X-WR-CALNAME:Holidays
X-WR-TIMEZONE:Etc/GMT
BEGIN:VEVENT
SUMMARY:New Year's Day
DTSTART:20220101
DTEND:20220101
DESCRIPTION:Happy New Year!
UID:636a0cc1dbd5a1667894465@icalendar
DTSTAMP:20221108T080105Z
STATUS:CONFIRMED
TRANSP:TRANSPARENT
SEQUENCE:0
END:VEVENT
BEGIN:VEVENT
SUMMARY:Orthodox Christmas
DTSTART:20220107
DTEND:20220107
LOCATION:Russia
DESCRIPTION:It is Christmas again!
UID:636a0cc1dbfd91667894465@icalendar
STATUS:CONFIRMED
TRANSP:TRANSPARENT
SEQUENCE:0
END:VEVENT
BEGIN:VEVENT
SUMMARY:International Women's Day
DTSTART:20220308
DTEND:20220308
DESCRIPTION:May the feminine be honoured!
UID:636a0cc1dc0f11667894465@icalendar
STATUS:CONFIRMED
TRANSP:TRANSPARENT
SEQUENCE:0
END:VEVENT
END:VCALENDAR
28 changes: 28 additions & 0 deletions src/icalendar/tests/calendars/issue_165_missing_event.ics
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
BEGIN:VCALENDAR
METHOD:REQUEST
PRODID:Microsoft CDO for Microsoft Exchange
VERSION:2.0
BEGIN:VTIMEZONE
TZID:GMT +0100 (Standard) / GMT +0200 (Daylight)
BEGIN:STANDARD
DTSTART:16010101T030000
TZOFFSETFROM:+0200
TZOFFSETTO:+0100
RRULE:FREQ=YEARLY;WKST=MO;INTERVAL=1;BYMONTH=10;BYDAY=-1SU
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:16010101T020000
TZOFFSETFROM:+0100
TZOFFSETTO:+0200
RRULE:FREQ=YEARLY;WKST=MO;INTERVAL=1;BYMONTH=3;BYDAY=-1SU
END:DAYLIGHT
END:VTIMEZONE
BEGIN:VEVENT
DTSTAMP:20150703T071009Z
DTSTART;TZID="GMT +0100 (Standard) / GMT +0200 (Daylight)":20150703T100000
SUMMARY:Sprint 25 Daily Standup
DTEND;TZID="GMT +0100 (Standard) / GMT +0200 (Daylight)":20150703T103000
RRULE:FREQ=DAILY;UNTIL=20150722T080000Z;INTERVAL=1;BYDAY=MO, TU, WE, TH, FR
;WKST=SU
END:VEVENT
END:VCALENDAR
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
BEGIN:VCALENDAR
VERSION:2.0
PRODID:icalendar-2023
BEGIN:VEVENT
UID:ical-jacadzaca-3
SUMMARY: Some very different event ':'
DTSTART;TZID="Western/Central Europe";VALUE=DATE-TIME:20211101T160000
DTEND;TZID="Western/Central Europe";VALUE=DATE-TIME:20211101T163000
DTSTAMP:20211004T150245Z
END:VEVENT
BEGIN:VEVENT
UID:ical-jacadzaca-4
SUMMARY: Some very different other event
DTSTART;TZID="Western/Central Europe";VALUE=DATE-TIME:20211101T164000
DTEND;TZID="Western/Central Europe";VALUE=DATE-TIME:20211101T165000
DTSTAMP:20211004T150245Z
END:VEVENT
END:VCALENDAR
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
BEGIN:VCALENDAR
VERSION:2.0
PRODID:icalendar-2023
BEGIN:VEVENT
UID:1
SUMMARY: Some event ':'
DTSTART;TZID="Western/Central Europe";VALUE=DATE-TIME:20211101T160000
DTEND;TZID="Western/Central Europe";VALUE=DATE-TIME:20211101T163000
DTSTAMP:20211004T150245Z
END:VEVENT
END:VCALENDAR
18 changes: 18 additions & 0 deletions src/icalendar/tests/calendars/issue_526_calendar_with_events.ics
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
BEGIN:VCALENDAR
VERSION:2.0
PRODID:icalendar-2023
BEGIN:VEVENT
UID:1
SUMMARY: Some event ':'
DTSTART;TZID="Western/Central Europe";VALUE=DATE-TIME:20211101T160000
DTEND;TZID="Western/Central Europe";VALUE=DATE-TIME:20211101T163000
DTSTAMP:20211004T150245Z
END:VEVENT
BEGIN:VEVENT
UID:2
SUMMARY: Some other event
DTSTART;TZID="Western/Central Europe";VALUE=DATE-TIME:20211101T164000
DTEND;TZID="Western/Central Europe";VALUE=DATE-TIME:20211101T165000
DTSTAMP:20211004T150245Z
END:VEVENT
END:VCALENDAR
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
BEGIN:VCALENDAR
VERSION:2.0
PRODID:icalendar-2023
BEGIN:VEVENT
UID:2
SUMMARY: Some other event
DTSTART;TZID="Western/Central Europe";VALUE=DATE-TIME:20211101T164000
DTEND;TZID="Western/Central Europe";VALUE=DATE-TIME:20211101T165000
DTSTAMP:20211004T150245Z
END:VEVENT
BEGIN:VEVENT
UID:1
SUMMARY: Some event ':'
DTSTART;TZID="Western/Central Europe";VALUE=DATE-TIME:20211101T160000
DTEND;TZID="Western/Central Europe";VALUE=DATE-TIME:20211101T163000
DTSTAMP:20211004T150245Z
END:VEVENT
END:VCALENDAR
Loading

0 comments on commit ccc3810

Please sign in to comment.