示例#1
0
    def _serialize_attrs(self, component: Component, context: ContextDict, container: Container):
        assert isinstance(component, Calendar)
        context.setdefault(DatetimeConverterMixin.CONTEXT_KEY_AVAILABLE_TZ, {})
        super()._serialize_attrs(component, context, container)

        # serialize all used timezones
        timezones = [tz.to_container() for tz in context[DatetimeConverterMixin.CONTEXT_KEY_AVAILABLE_TZ].values()]
        # insert them at the place where they usually would have been serialized
        split = context["VTIMEZONES_AFTER"]
        container.data = container.data[:split] + timezones + container.data[split:]
示例#2
0
 def post_populate(self, component: Component, context: ContextDict):
     lines_str = "".join(
         line.serialize(newline=True)
         for line in context.pop((self, "lines")))
     # TODO only feed dateutil the params it likes, add the rest as extra
     tzinfos = context.get(DatetimeConverterMixin.CONTEXT_KEY_AVAILABLE_TZ,
                           {})
     rrule = dateutil.rrule.rrulestr(lines_str,
                                     tzinfos=tzinfos,
                                     compatible=True)
     rrule._rdate = list(unique_justseen(sorted(rrule._rdate)))
     rrule._exdate = list(unique_justseen(sorted(rrule._exdate)))
     self.set_or_append_value(component, rrule)
示例#3
0
 def post_populate(self, component: Component, context: ContextDict):
     timespan_type = getattr(component, "_TIMESPAN_TYPE", self.value_type)
     timespan = timespan_type(ensure_datetime(context[CONTEXT_BEGIN_TIME]),
                              ensure_datetime(context[CONTEXT_END_TIME]),
                              context[CONTEXT_DURATION],
                              context[CONTEXT_PRECISION])
     # missing values will be reported by the Timespan validator
     if context[CONTEXT_END_NAME] and context[
             CONTEXT_END_NAME] != timespan._end_name():
         raise ValueError("expected to get %s value, but got %s instead" %
                          (timespan._end_name(), context[CONTEXT_END_NAME]))
     self.set_or_append_value(component, timespan)
     # we need to clear all values, otherwise they might not get overwritten by the next parsed Timespan
     for key in CONTEXT_KEYS:
         context.pop(key, None)
示例#4
0
 def finalize(self, component: "Component", context: ContextDict):
     self._check_component(component, context)
     # missing values will be reported by the Timespan validator
     timespan = self.value_type(
         ensure_datetime(context[CONTEXT_BEGIN_TIME]),
         ensure_datetime(context[CONTEXT_END_TIME]),
         context[CONTEXT_DURATION], context[CONTEXT_PRECISION])
     if context[CONTEXT_END_NAME] and context[
             CONTEXT_END_NAME] != timespan._end_name():
         raise ValueError("expected to get %s value, but got %s instead" %
                          (timespan._end_name(), context[CONTEXT_END_NAME]))
     self.set_or_append_value(component, timespan)
     super(TimespanConverter, self).finalize(component, context)
     # we need to clear all values, otherwise they might not get overwritten by the next parsed Timespan
     for key in CONTEXT_KEYS:
         context.pop(key, None)
示例#5
0
 def serialize_toplevel(self, component: "Component", context: Optional[ContextDict] = None):
     if not context:
         context = ContextDict(defaultdict(lambda: None))
     container = Container(self.container_name)
     for conv in self.converters:
         conv.serialize(component, container, context)
     container.extend(component.extra)
     return container
示例#6
0
    def populate(self, component: Component, item: ContainerItem,
                 context: ContextDict) -> bool:
        assert isinstance(item, ContentLine)
        seen_items = context.setdefault(CONTEXT_ITEMS, set())
        if item.name in seen_items:
            raise ValueError("duplicate value for %s in %s" %
                             (item.name, item))
        seen_items.add(item.name)

        params = copy_extra_params(item.params)
        if item.name in ["DTSTART", "DTEND", "DUE"]:
            value_type = params.pop("VALUE", ["DATE-TIME"])
            if value_type == ["DATE-TIME"]:
                precision = "second"
                value = DatetimeConverter.parse(item.value, params, context)
            elif value_type == ["DATE"]:
                precision = "day"
                value = DateConverter.parse(item.value, params, context)
            else:
                raise ValueError("can't handle %s with value type %s" %
                                 (item.name, value_type))

            if context.setdefault(CONTEXT_PRECISION, precision) != precision:
                raise ValueError(
                    "event with diverging begin and end time precision")

            if item.name == "DTSTART":
                self.set_or_append_extra_params(component,
                                                params,
                                                name="begin")
                context[CONTEXT_BEGIN_TIME] = value
            else:
                end_name = {"DTEND": "end", "DUE": "due"}[item.name]
                context[CONTEXT_END_NAME] = end_name
                self.set_or_append_extra_params(component,
                                                params,
                                                name=end_name)
                context[CONTEXT_END_TIME] = value

        else:
            assert item.name == "DURATION"
            self.set_or_append_extra_params(component, params, name="duration")
            context[CONTEXT_DURATION] = DurationConverter.parse(
                item.value, params, context)

        return True
示例#7
0
    def _populate_attrs(self, instance: Component, container: Container, context: ContextDict):
        assert isinstance(instance, Calendar)
        avail_tz: Dict[str, Timezone] = context.setdefault(DatetimeConverterMixin.CONTEXT_KEY_AVAILABLE_TZ, {})
        for child in container:
            if child.name == Timezone.NAME and isinstance(child, Container):
                tz = Timezone.from_container(child)
                avail_tz.setdefault(tz.tzid, tz)

        super()._populate_attrs(instance, container, context)
示例#8
0
 def serialize_toplevel(self,
                        component: Component,
                        context: Optional[ContextDict] = None):
     check_is_instance("instance", component, self.component_type)
     if not context:
         context = ContextDict(defaultdict(lambda: None))
     container = Container(
         component.extra.name
     )  # allow overwriting the name by setting the name of the extras
     self._serialize_attrs(component, context, container)
     return container
示例#9
0
    def populate_instance(self,
                          instance: Component,
                          container: Container,
                          context: Optional[ContextDict] = None):
        if container.name != self.component_type.NAME:
            raise ValueError("container {} is no {}".format(
                container.name, self.component_type.NAME))
        check_is_instance("instance", instance,
                          (self.component_type, MutablePseudoComponent))
        if not context:
            context = ContextDict(defaultdict(lambda: None))

        self._populate_attrs(instance, container, context)
示例#10
0
 def _serialize_dt(self, value: datetime, params: ExtraParams, context: ContextDict,
                   utc_fmt="%Y%m%dT%H%M%SZ", nonutc_fmt="%Y%m%dT%H%M%S") -> str:
     if is_utc(value):
         return value.strftime(utc_fmt)
     else:
         if value.tzinfo is not None:
             tzname = value.tzinfo.tzname(value)
             if not tzname:
                 # TODO generate unique identifier as name
                 raise ValueError("could not generate name for tzinfo %s" % value.tzinfo)
             params["TZID"] = [tzname]
             available_tz = context.setdefault(self.CONTEXT_KEY_AVAILABLE_TZ, {})
             available_tz.setdefault(tzname, value.tzinfo)
         return value.strftime(nonutc_fmt)
示例#11
0
    def populate_instance(self, instance: "Component", container: Container, context: Optional[ContextDict] = None):
        if container.name != self.container_name:
            raise ValueError("container isn't an {}".format(self.container_name))
        if not context:
            context = ContextDict(defaultdict(lambda: None))

        for line in container:
            consumed = False
            for conv in self.converter_lookup[line.name]:
                if conv.populate(instance, line, context):
                    consumed = True
            if not consumed:
                instance.extra.append(line)

        for conv in self.converters:
            conv.finalize(instance, context)
示例#12
0
    def _serialize_dt(self,
                      value: datetime,
                      params: ExtraParams,
                      context: ContextDict,
                      utc_fmt="%Y%m%dT%H%M%SZ",
                      nonutc_fmt="%Y%m%dT%H%M%S") -> str:
        if is_utc(value):
            return value.strftime(utc_fmt)

        if value.tzinfo is not None:
            tz = Timezone.from_tzinfo(value.tzinfo, context)
            if tz is not None:
                params["TZID"] = [tz.tzid]
                available_tz = context.setdefault(
                    self.CONTEXT_KEY_AVAILABLE_TZ, {})
                available_tz.setdefault(tz.tzid, tz)
        return value.strftime(nonutc_fmt)
示例#13
0
    def _parse_dt(self,
                  value: str,
                  params: ExtraParams,
                  context: ContextDict,
                  warn_no_avail_tz=True) -> datetime:
        param_tz_list: Optional[List[str]] = params.pop(
            "TZID", None)  # we remove the TZID from context
        if param_tz_list:
            if len(param_tz_list) > 1:
                raise ValueError("got multiple TZIDs")
            param_tz: Optional[str] = param_tz_list[0]
        else:
            param_tz = None
        available_tz = context.get(self.CONTEXT_KEY_AVAILABLE_TZ, None)
        if available_tz is None and warn_no_avail_tz:
            warnings.warn(
                "DatetimeConverterMixin.parse called without available_tz dict in context"
            )
        fixed_utc = (value[-1].upper() == 'Z')

        value = value.translate({
            ord("/"): "",
            ord("-"): "",
            ord("Z"): "",
            ord("z"): ""
        })
        dt = datetime.strptime(value, self.FORMATS[len(value)])

        if fixed_utc:
            if param_tz:
                raise ValueError(
                    "can't specify UTC via appended 'Z' and TZID param '%s'" %
                    param_tz)
            return dt.replace(tzinfo=dateutil_tzutc)
        elif param_tz:
            selected_tz = None
            if available_tz:
                selected_tz = available_tz.get(param_tz, None)
            if selected_tz is None:
                selected_tz = gettz(
                    param_tz)  # be lenient with missing vtimezone definitions
            return dt.replace(tzinfo=selected_tz)
        else:
            return dt
示例#14
0
 def post_serialize(self, component: Component, output: Container,
                    context: ContextDict):
     context.pop("DTSTART", None)
示例#15
0
    def _parse_dt(self,
                  value: str,
                  params: ExtraParams,
                  context: ContextDict,
                  warn_no_avail_tz=True) -> datetime:
        param_tz_list: Optional[List[str]] = params.pop(
            "TZID", None)  # we remove the TZID from context
        if param_tz_list:
            if len(param_tz_list) > 1:
                raise ValueError("got multiple TZIDs")
            param_tz: Optional[str] = str(
                param_tz_list[0])  # convert QuotedParamValues
        else:
            param_tz = None
        available_tz = context.setdefault(self.CONTEXT_KEY_AVAILABLE_TZ, {})
        if available_tz is None and warn_no_avail_tz:
            warnings.warn(
                "DatetimeConverterMixin.parse called without available_tz dict in context"
            )
        fixed_utc = (value[-1].upper() == 'Z')

        tr_value = value.translate({
            ord("/"): "",
            ord("-"): "",
            ord("Z"): "",
            ord("z"): ""
        })
        try:
            format = self.FORMATS[len(tr_value)]
        except KeyError:
            raise ValueError(
                "couldn't find format matching %r (%s chars), tried %s" %
                (tr_value, len(tr_value), self.FORMATS))
        dt = datetime.strptime(tr_value, format)

        if fixed_utc:
            if param_tz:
                raise ValueError(
                    "can't specify UTC via appended 'Z' and TZID param '%s'" %
                    param_tz)
            return dt.replace(tzinfo=dateutil_tzutc)
        elif param_tz:
            selected_tz = None
            if available_tz:
                selected_tz = available_tz.get(param_tz, None)
            if selected_tz is None:
                try:
                    selected_tz = Timezone.from_tzid(
                        param_tz
                    )  # be lenient with missing vtimezone definitions
                except ValueError:
                    found_tz = gettz(param_tz)
                    import platform
                    import sys
                    if not found_tz:
                        raise ValueError(
                            "timezone %s is unknown on this system (%s on %s)"
                            % (param_tz, sys.version,
                               platform.platform(terse=True)))
                    else:
                        warnings.warn(
                            "no ics representation available for timezone %s, but known to system (%s on %s) as %s "
                            % (param_tz, sys.version,
                               platform.platform(terse=True), found_tz))
                    selected_tz = found_tz
                available_tz.setdefault(param_tz, selected_tz)
            return dt.replace(tzinfo=selected_tz)
        else:
            return dt