From 8a5ae73f34c8f9f075a15cc06ef672c08f1d83d4 Mon Sep 17 00:00:00 2001 From: Yushin Washio Date: Sun, 26 Nov 2023 22:39:37 +0100 Subject: [PATCH 1/3] Use filter and join instead of concatenating --- caldav/lib/vcal.py | 46 ++++++++++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/caldav/lib/vcal.py b/caldav/lib/vcal.py index 7c9f2298..c1091736 100644 --- a/caldav/lib/vcal.py +++ b/caldav/lib/vcal.py @@ -76,25 +76,11 @@ def fix(event): ## 3 fix duplicated DTSTAMP ... and ... ## 5 prepare to remove DURATION or DTEND/DUE if both DURATION and ## DTEND/DUE is set. - ## OPTIMIZATION TODO: use list and join rather than concatenation ## remove duplication of DTSTAMP - fixed2 = "" - for line in fixed.strip().split("\n"): - if line.startswith("BEGIN:V"): - stamped = 0 - ended = 0 - - elif re.search("^(DURATION|DTEND|DUE)[:;]", line): - if ended: - continue - ended += 1 - - elif line.startswith("DTSTAMP") and line[7] in (";", ":"): - if stamped: - continue - stamped += 1 - - fixed2 += line + "\n" + fixed2 = ( + "\n".join(filter(LineFilterDiscardingDuplicates(), fixed.strip().split("\n"))) + + "\n" + ) if fixed2 != event: global fixup_error_loggings @@ -129,6 +115,30 @@ def fix(event): return fixed2 +class LineFilterDiscardingDuplicates: + """Needs to be a class because it keeps track of whether a certain + group of date line was already encountered within a vobject. + This must be called line by line in order on the complete text, at + least comprising the complete vobject. + """ + def __call__(self, line): + if line.startswith("BEGIN:V"): + self.stamped = 0 + self.ended = 0 + + elif re.search("^(DURATION|DTEND|DUE)[:;]", line): + if self.ended: + return False + self.ended += 1 + + elif line.startswith("DTSTAMP") and line[7] in (";", ":"): + if self.stamped: + return False + self.stamped += 1 + + return True + + ## sorry for being english-language-euro-centric ... fits rather perfectly as default language for me :-) def create_ical(ical_fragment=None, objtype=None, language="en_DK", **props): """ From dcde747655a97b8e42f2770e53c45f4d0ca1e7c4 Mon Sep 17 00:00:00 2001 From: Yushin Washio Date: Sun, 26 Nov 2023 22:42:44 +0100 Subject: [PATCH 2/3] Initialize stamped and ended MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Only necessary for bad syntax, but this function shouldn’t fail like that. --- caldav/lib/vcal.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/caldav/lib/vcal.py b/caldav/lib/vcal.py index c1091736..5de3286d 100644 --- a/caldav/lib/vcal.py +++ b/caldav/lib/vcal.py @@ -121,6 +121,11 @@ class LineFilterDiscardingDuplicates: This must be called line by line in order on the complete text, at least comprising the complete vobject. """ + + def __init__(self): + self.stamped = 0 + self.ended = 0 + def __call__(self, line): if line.startswith("BEGIN:V"): self.stamped = 0 From d9e485c6ea3e846fe8606889b0a591f374fafa54 Mon Sep 17 00:00:00 2001 From: Yushin Washio Date: Sun, 26 Nov 2023 22:48:39 +0100 Subject: [PATCH 3/3] Simplify/unify match logic a bit --- caldav/lib/vcal.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/caldav/lib/vcal.py b/caldav/lib/vcal.py index 5de3286d..764fcc2b 100644 --- a/caldav/lib/vcal.py +++ b/caldav/lib/vcal.py @@ -131,12 +131,12 @@ def __call__(self, line): self.stamped = 0 self.ended = 0 - elif re.search("^(DURATION|DTEND|DUE)[:;]", line): + elif re.match("(DURATION|DTEND|DUE)[:;]", line): if self.ended: return False self.ended += 1 - elif line.startswith("DTSTAMP") and line[7] in (";", ":"): + elif re.match("DTSTAMP[:;]", line): if self.stamped: return False self.stamped += 1