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]) 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): if all(type(d) is type(ref_date) for d in dates.value):
continue 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): for i, date in enumerate(dates.value):
dates.value[i] = ref_date.replace( dates.value[i] = ref_date.replace(
date.year, date.month, date.day) 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 string import ascii_letters, digits, punctuation
from typing import Callable, Sequence, Tuple, Type, TypeVar, Union from typing import Callable, Sequence, Tuple, Type, TypeVar, Union
import vobject
from radicale import config from radicale import config
from radicale.log import logger from radicale.log import logger
@@ -107,6 +109,43 @@ def vobject_supports_vcard4() -> bool:
return False 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(): def packages_version():
versions = [] versions = []
versions.append("python=%s.%s.%s" % (sys.version_info[0], sys.version_info[1], sys.version_info[2])) versions.append("python=%s.%s.%s" % (sys.version_info[0], sys.version_info[1], sys.version_info[2]))