add test whether vobject supports PERIOD

This commit is contained in:
Peter Bieringer
2026-06-21 16:30:53 +02:00
parent 47fdd34058
commit 8bcd43742a
2 changed files with 60 additions and 0 deletions

View File

@@ -200,6 +200,27 @@ def check_and_sanitize_items(
logger.trace("ITEM/check_and_sanitize_item: remove existing tzinfo (dtstart has none): '%s' -> '%s'", date, dates.value[i])
if all(type(d) is type(ref_date) for d in dates.value):
continue
if dates.params.get("VALUE") == ["PERIOD"]:
if not utils.vobject_supports_period():
raise ValueError("PERIOD not supported by used vobject=%s in object %r" % (utils.package_version("vobject"), component_uid))
# period = period-explicit / period-start
# period-explicit = date-time "/" date-time
# period-start = date-time "/" dur-value
for i, date in enumerate(dates.value):
if not isinstance(date, tuple):
raise ValueError("invalid PERIOD (not a tuple) in object %r" % component_uid)
if len(date) != 2:
raise ValueError("invalid PERIOD (not 2 elements) in object %r" % component_uid)
if type(date[0]) is datetime.datetime and type(date[1]) is datetime.datetime:
if (date[0] > date[1]):
raise ValueError("invalid PERIOD (end before start) in object %r" % component_uid)
# skip explicit tzinfo check for now, no buggy client known
logger.trace("ITEM/check_and_sanitize_item: PERIOD/start-stop found: '%s'", date)
elif type(date[0]) is datetime.datetime and type(date[1]) is datetime.timedelta:
logger.trace("ITEM/check_and_sanitize_item: PERIOD/start-duration found: '%s'", date)
else:
raise ValueError("invalid PERIOD (element types not matching) in object %r" % component_uid)
continue
for i, date in enumerate(dates.value):
dates.value[i] = ref_date.replace(
date.year, date.month, date.day)

View File

@@ -27,6 +27,8 @@ from importlib import import_module, metadata
from string import ascii_letters, digits, punctuation
from typing import Callable, Sequence, Tuple, Type, TypeVar, Union
import vobject
from radicale import config
from radicale.log import logger
@@ -107,6 +109,43 @@ def vobject_supports_vcard4() -> bool:
return False
# global cache
vobject_supports_period_cache: Union[bool, None] = None
def vobject_supports_period() -> bool:
"""Check if vobject supports period (requires version > 0.9.9)."""
global vobject_supports_period_cache
if vobject_supports_period_cache is not None:
return vobject_supports_period_cache
content_test = """
BEGIN:VCALENDAR
VERSION:2.0
PRODID:test
BEGIN:VEVENT
UID:test-rdate-period-time-start-stop
DTSTART:20000101T000000Z
DURATION:PT1H
DESCRIPTION:event with rdate period start and stop
DTSTAMP:20000101T000000Z
RDATE;VALUE=PERIOD:20000102T000000Z/20000402T000000Z
RRULE:FREQ=WEEKLY
END:VEVENT
END:VCALENDAR
"""
obj = vobject.readOne(content_test)
try:
obj.serialize()
except Exception:
vobject_supports_period_cache = False
else:
vobject_supports_period_cache = True
return vobject_supports_period_cache
def packages_version():
versions = []
versions.append("python=%s.%s.%s" % (sys.version_info[0], sys.version_info[1], sys.version_info[2]))