Ray 1 год назад
Родитель
Сommit
d6c0a05771
5 измененных файлов с 26 добавлено и 14 удалено
  1. 12 6
      radicale/app/report.py
  2. 1 0
      radicale/config.py
  3. 3 0
      radicale/item/filter.py
  4. 1 1
      radicale/tests/__init__.py
  5. 9 7
      radicale/tests/test_base.py

+ 12 - 6
radicale/app/report.py

@@ -23,26 +23,31 @@ import datetime
 import posixpath
 import socket
 import xml.etree.ElementTree as ET
-import vobject
 from http import client
 from typing import (Any, Callable, Iterable, Iterator, List, Optional,
                     Sequence, Tuple, Union)
 from urllib.parse import unquote, urlparse
 
+import vobject
 import vobject.base
 from vobject.base import ContentLine
 
 import radicale.item as radicale_item
-from radicale import httputils, pathutils, storage, types, xmlutils, config
+from radicale import httputils, pathutils, storage, types, xmlutils
 from radicale.app.base import Access, ApplicationBase
 from radicale.item import filter as radicale_filter
 from radicale.log import logger
 
+
 def free_busy_report(base_prefix: str, path: str, xml_request: Optional[ET.Element],
                      collection: storage.BaseCollection, encoding: str,
                      unlock_storage_fn: Callable[[], None],
                      max_occurrence: int
-                     ) -> Tuple[int, str]:
+                     ) -> Tuple[int, Union[ET.Element, str]]:
+    # NOTE: this function returns both an Element and a string because
+    # free-busy reports are an edge-case on the return type according
+    # to the spec.
+
     multistatus = ET.Element(xmlutils.make_clark("D:multistatus"))
     if xml_request is None:
         return client.MULTI_STATUS, multistatus
@@ -54,15 +59,16 @@ def free_busy_report(base_prefix: str, path: str, xml_request: Optional[ET.Eleme
         return client.FORBIDDEN, xmlutils.webdav_error("D:supported-report")
 
     time_range_element = root.find(xmlutils.make_clark("C:time-range"))
+    assert isinstance(time_range_element, ET.Element)
 
     # Build a single filter from the free busy query for retrieval
     # TODO: filter for VFREEBUSY in additional to VEVENT but
     # test_filter doesn't support that yet.
     vevent_cf_element = ET.Element(xmlutils.make_clark("C:comp-filter"),
-                                   attrib={'name':'VEVENT'})
+                                   attrib={'name': 'VEVENT'})
     vevent_cf_element.append(time_range_element)
     vcalendar_cf_element = ET.Element(xmlutils.make_clark("C:comp-filter"),
-                                   attrib={'name':'VCALENDAR'})
+                                      attrib={'name': 'VCALENDAR'})
     vcalendar_cf_element.append(vevent_cf_element)
     filter_element = ET.Element(xmlutils.make_clark("C:filter"))
     filter_element.append(vcalendar_cf_element)
@@ -525,7 +531,7 @@ class ApplicationPartReport(ApplicationBase):
                         "Bad REPORT request on %r: %s", path, e, exc_info=True)
                     return httputils.BAD_REQUEST
                 headers = {"Content-Type": "text/calendar; charset=%s" % self._encoding}
-                return status, headers, body
+                return status, headers, str(body)
             else:
                 try:
                     status, xml_answer = xml_report(

+ 1 - 0
radicale/config.py

@@ -301,6 +301,7 @@ DEFAULT_CONFIG_SCHEMA: types.CONFIG_SCHEMA = OrderedDict([
             "type": positive_int})]))
     ])
 
+
 def parse_compound_paths(*compound_paths: Optional[str]
                          ) -> List[Tuple[str, bool]]:
     """Parse a compound path and return the individual paths.

+ 3 - 0
radicale/item/filter.py

@@ -70,10 +70,12 @@ def parse_time_range(time_filter: ET.Element) -> Tuple[datetime, datetime]:
         end = DATETIME_MAX
     return start, end
 
+
 def time_range_timestamps(time_filter: ET.Element) -> Tuple[int, int]:
     start, end = parse_time_range(time_filter)
     return (math.floor(start.timestamp()), math.ceil(end.timestamp()))
 
+
 def comp_match(item: "item.Item", filter_: ET.Element, level: int = 0) -> bool:
     """Check whether the ``item`` matches the comp ``filter_``.
 
@@ -202,6 +204,7 @@ def time_range_fill(vobject_item: vobject.base.Component,
 
     start, end = parse_time_range(filter_)
     ranges: List[Tuple[datetime, datetime]] = []
+
     def range_fn(range_start: datetime, range_end: datetime,
                  is_recurrence: bool) -> bool:
         nonlocal ranges

+ 1 - 1
radicale/tests/__init__.py

@@ -27,11 +27,11 @@ import sys
 import tempfile
 import wsgiref.util
 import xml.etree.ElementTree as ET
-import vobject
 from io import BytesIO
 from typing import Any, Dict, List, Optional, Tuple, Union
 
 import defusedxml.ElementTree as DefusedET
+import vobject
 
 import radicale
 from radicale import app, config, types, xmlutils

+ 9 - 7
radicale/tests/test_base.py

@@ -22,10 +22,10 @@ Radicale tests with simple requests.
 
 import os
 import posixpath
-import vobject
 from typing import Any, Callable, ClassVar, Iterable, List, Optional, Tuple
 
 import defusedxml.ElementTree as DefusedET
+import vobject
 
 from radicale import storage, xmlutils
 from radicale.tests import RESPONSES, BaseTest
@@ -1369,7 +1369,7 @@ permissions: RrWw""")
         """Test free busy report on a few items"""
         calendar_path = "/calendar.ics/"
         self.mkcalendar(calendar_path)
-        for i in (1,2,10):
+        for i in (1, 2, 10):
             filename = "event{}.ics".format(i)
             event = get_file_content(filename)
             self.put(posixpath.join(calendar_path, filename), event)
@@ -1377,18 +1377,20 @@ permissions: RrWw""")
 <?xml version="1.0" encoding="utf-8" ?>
 <C:free-busy-query xmlns:C="urn:ietf:params:xml:ns:caldav">
     <C:time-range start="20130901T140000Z" end="20130908T220000Z"/>
-</C:free-busy-query>""", 200, is_xml = False)
+</C:free-busy-query>""", 200, is_xml=False)
         for response in responses.values():
             assert isinstance(response, vobject.base.Component)
         assert len(responses) == 1
         vcalendar = list(responses.values())[0]
+        assert isinstance(vcalendar, vobject.base.Component)
         assert len(vcalendar.vfreebusy_list) == 3
         types = {}
         for vfb in vcalendar.vfreebusy_list:
-            if vfb.fbtype.value not in types:
-                types[vfb.fbtype.value] = 0
-            types[vfb.fbtype.value] += 1
-        assert types == {'BUSY':2, 'FREE':1}
+            fbtype_val = vfb.fbtype.value
+            if fbtype_val not in types:
+                types[fbtype_val] = 0
+            types[fbtype_val] += 1
+        assert types == {'BUSY': 2, 'FREE': 1}
 
     def _report_sync_token(
             self, calendar_path: str, sync_token: Optional[str] = None