Skip to content

Commit b1567f8

Browse files
Revert "Apply ruff formatting to issue #867 fix"
This reverts commit 2cc6d82.
1 parent 338e5e5 commit b1567f8

4 files changed

Lines changed: 87 additions & 94 deletions

File tree

src/icalendar/cal/event.py

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
import uuid
66
from datetime import date, datetime, timedelta
7-
from typing import TYPE_CHECKING, Sequence
7+
from typing import TYPE_CHECKING, Optional, Sequence
88

99
from icalendar.attr import (
1010
X_MOZ_LASTACK_property,
@@ -309,9 +309,9 @@ def duration(self) -> timedelta:
309309
Returns the DURATION property if set, otherwise calculated from start and end.
310310
"""
311311
# First check if DURATION property is explicitly set
312-
if "DURATION" in self:
313-
return self["DURATION"].dt
314-
312+
if 'DURATION' in self:
313+
return self['DURATION'].dt
314+
315315
# Fall back to calculated duration from start and end
316316
return self.end - self.start
317317

@@ -343,7 +343,7 @@ def start(self) -> date | datetime:
343343
return start
344344

345345
@start.setter
346-
def start(self, start: date | datetime | None):
346+
def start(self, start: Optional[date | datetime]):
347347
"""Set the start."""
348348
self.DTSTART = start
349349

@@ -397,27 +397,27 @@ def end(self, end: date | datetime | None):
397397
def new(
398398
cls,
399399
/,
400-
attendees: list[vCalAddress] | None = None,
400+
attendees: Optional[list[vCalAddress]] = None,
401401
categories: Sequence[str] = (),
402-
classification: CLASS | None = None,
403-
color: str | None = None,
402+
classification: Optional[CLASS] = None,
403+
color: Optional[str] = None,
404404
comments: list[str] | str | None = None,
405405
contacts: list[str] | str | None = None,
406-
created: date | None = None,
407-
description: str | None = None,
408-
end: date | datetime | None = None,
409-
last_modified: date | None = None,
410-
location: str | None = None,
411-
organizer: vCalAddress | str | None = None,
412-
priority: int | None = None,
413-
sequence: int | None = None,
414-
stamp: date | None = None,
415-
start: date | datetime | None = None,
416-
status: STATUS | None = None,
417-
transparency: TRANSP | None = None,
418-
summary: str | None = None,
419-
uid: str | uuid.UUID | None = None,
420-
url: str | None = None,
406+
created: Optional[date] = None,
407+
description: Optional[str] = None,
408+
end: Optional[date | datetime] = None,
409+
last_modified: Optional[date] = None,
410+
location: Optional[str] = None,
411+
organizer: Optional[vCalAddress | str] = None,
412+
priority: Optional[int] = None,
413+
sequence: Optional[int] = None,
414+
stamp: Optional[date] = None,
415+
start: Optional[date | datetime] = None,
416+
status: Optional[STATUS] = None,
417+
transparency: Optional[TRANSP] = None,
418+
summary: Optional[str] = None,
419+
uid: Optional[str | uuid.UUID] = None,
420+
url: Optional[str] = None,
421421
):
422422
"""Create a new event with all required properties.
423423

src/icalendar/cal/todo.py

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
import uuid
66
from datetime import date, datetime, timedelta
7-
from typing import TYPE_CHECKING, Sequence
7+
from typing import TYPE_CHECKING, Optional, Sequence
88

99
from icalendar.attr import (
1010
X_MOZ_LASTACK_property,
@@ -182,7 +182,7 @@ def start(self) -> date | datetime:
182182
return start
183183

184184
@start.setter
185-
def start(self, start: date | datetime | None):
185+
def start(self, start: Optional[date | datetime]):
186186
"""Set the start."""
187187
self.DTSTART = start
188188

@@ -219,9 +219,9 @@ def duration(self) -> timedelta:
219219
For todos, DURATION can exist without DTSTART per RFC 5545.
220220
"""
221221
# First check if DURATION property is explicitly set
222-
if "DURATION" in self:
223-
return self["DURATION"].dt
224-
222+
if 'DURATION' in self:
223+
return self['DURATION'].dt
224+
225225
# Fall back to calculated duration from start and end
226226
return self.end - self.start
227227

@@ -268,26 +268,26 @@ def alarms(self) -> Alarms:
268268
def new(
269269
cls,
270270
/,
271-
attendees: list[vCalAddress] | None = None,
271+
attendees: Optional[list[vCalAddress]] = None,
272272
categories: Sequence[str] = (),
273-
classification: CLASS | None = None,
274-
color: str | None = None,
273+
classification: Optional[CLASS] = None,
274+
color: Optional[str] = None,
275275
comments: list[str] | str | None = None,
276276
contacts: list[str] | str | None = None,
277-
created: date | None = None,
278-
description: str | None = None,
279-
end: date | datetime | None = None,
280-
last_modified: date | None = None,
281-
location: str | None = None,
282-
organizer: vCalAddress | str | None = None,
283-
priority: int | None = None,
284-
sequence: int | None = None,
285-
stamp: date | None = None,
286-
start: date | datetime | None = None,
287-
status: STATUS | None = None,
288-
summary: str | None = None,
289-
uid: str | uuid.UUID | None = None,
290-
url: str | None = None,
277+
created: Optional[date] = None,
278+
description: Optional[str] = None,
279+
end: Optional[date | datetime] = None,
280+
last_modified: Optional[date] = None,
281+
location: Optional[str] = None,
282+
organizer: Optional[vCalAddress | str] = None,
283+
priority: Optional[int] = None,
284+
sequence: Optional[int] = None,
285+
stamp: Optional[date] = None,
286+
start: Optional[date | datetime] = None,
287+
status: Optional[STATUS] = None,
288+
summary: Optional[str] = None,
289+
uid: Optional[str | uuid.UUID] = None,
290+
url: Optional[str] = None,
291291
):
292292
"""Create a new TODO with all required properties.
293293

src/icalendar/tests/test_issue_662_component_properties.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -408,14 +408,14 @@ def test_incomplete_event(incomplete_event_end, attr):
408408
"component_with_duration",
409409
[
410410
incomplete_event_2, # Event with DURATION property
411-
incomplete_todo_2, # Todo with DURATION property
411+
incomplete_todo_2, # Todo with DURATION property
412412
],
413413
)
414414
def test_duration_property_accessible_without_dtstart(component_with_duration):
415415
"""Test that DURATION property is accessible even without DTSTART (fixes issue #867)."""
416416
# DURATION property should be accessible directly
417417
assert component_with_duration.duration == timedelta(hours=1)
418-
418+
419419
# But start and end should still raise errors for incomplete components
420420
with pytest.raises(IncompleteComponent):
421421
_ = component_with_duration.start
Lines changed: 40 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
"""Tests for issue #867: Todo.duration should work without DTSTART."""
22

3-
from datetime import datetime, timedelta
4-
53
import pytest
4+
from datetime import datetime, timedelta
65

7-
from icalendar import Event, Todo
6+
from icalendar import Todo, Event
87
from icalendar.error import IncompleteComponent
98

109

@@ -26,43 +25,43 @@ def test_todo_duration_without_dtstart():
2625
def test_todo_duration_calculated_from_start_and_due():
2726
"""Test that Todo.duration still works for calculated duration from `DTSTART` and `DUE`."""
2827
todo = Todo()
29-
todo.add("UID", "test-calculated")
28+
todo.add('UID', 'test-calculated')
3029
todo.start = datetime(2026, 3, 19, 12, 0)
3130
todo.end = datetime(2026, 3, 19, 15, 30)
32-
31+
3332
# Should calculate duration from start and end
3433
assert todo.duration == timedelta(hours=3, minutes=30)
3534

3635

3736
def test_todo_duration_prefers_duration_property():
3837
"""Test that explicit `DURATION` property takes precedence over calculated duration."""
3938
todo = Todo()
40-
todo.add("UID", "test-precedence")
39+
todo.add('UID', 'test-precedence')
4140
todo.start = datetime(2026, 3, 19, 12, 0)
4241
todo.end = datetime(2026, 3, 19, 15, 0) # This would be 3 hours
43-
todo.add("DURATION", timedelta(days=2)) # But DURATION says 2 days
44-
42+
todo.add('DURATION', timedelta(days=2)) # But DURATION says 2 days
43+
4544
# Should return DURATION property, not calculated value
4645
assert todo.duration == timedelta(days=2)
4746

4847

4948
def test_todo_duration_with_dtstart_and_duration():
5049
"""Test Todo with `DTSTART` and `DURATION` (valid per RFC 5545)."""
5150
todo = Todo()
52-
todo.add("UID", "test-start-duration")
51+
todo.add('UID', 'test-start-duration')
5352
todo.start = datetime(2026, 3, 19, 12, 0)
54-
todo.add("DURATION", timedelta(hours=4))
55-
53+
todo.add('DURATION', timedelta(hours=4))
54+
5655
# Should return DURATION property
5756
assert todo.duration == timedelta(hours=4)
5857

5958

6059
def test_todo_duration_without_any_time_info_raises_error():
6160
"""Test that Todo.duration raises error when no time information is available."""
6261
todo = Todo()
63-
todo.add("UID", "test-no-time")
64-
todo.add("SUMMARY", "Task without any time info")
65-
62+
todo.add('UID', 'test-no-time')
63+
todo.add('SUMMARY', 'Task without any time info')
64+
6665
# Should raise error since no DURATION, DTSTART, or DUE is set
6766
with pytest.raises(IncompleteComponent):
6867
_ = todo.duration
@@ -76,32 +75,29 @@ def test_todo_duration_complex_duration_values():
7675
("PT1H30M", timedelta(hours=1, minutes=30)),
7776
("P1DT2H", timedelta(days=1, hours=2)),
7877
("P1W", timedelta(weeks=1)),
79-
(
80-
"P1Y2M3DT4H5M6S",
81-
timedelta(days=428, hours=4, minutes=5, seconds=6),
82-
), # Approx 1 year 2 months
78+
("P1Y2M3DT4H5M6S", timedelta(days=428, hours=4, minutes=5, seconds=6)), # Approx 1 year 2 months
8379
]
84-
80+
8581
for duration_str, expected_delta in test_cases:
8682
todo = Todo()
87-
todo.add("UID", f"test-{duration_str}")
88-
todo.add("DURATION", expected_delta)
89-
83+
todo.add('UID', f'test-{duration_str}')
84+
todo.add('DURATION', expected_delta)
85+
9086
assert todo.duration == expected_delta
9187

9288

9389
def test_todo_duration_maintains_backward_compatibility():
9490
"""Test that the fix doesn't break existing functionality."""
9591
# Create todo the old way (DTSTART + DUE)
9692
todo = Todo()
97-
todo.add("UID", "test-backward-compat")
98-
todo.add("SUMMARY", "Backward compatibility test")
93+
todo.add('UID', 'test-backward-compat')
94+
todo.add('SUMMARY', 'Backward compatibility test')
9995
todo.start = datetime(2026, 3, 19, 9, 0)
10096
todo.end = datetime(2026, 3, 19, 17, 0)
101-
97+
10298
# Should still work as before
10399
assert todo.duration == timedelta(hours=8)
104-
100+
105101
# Properties should still work
106102
assert todo.start == datetime(2026, 3, 19, 9, 0)
107103
assert todo.end == datetime(2026, 3, 19, 17, 0)
@@ -110,9 +106,9 @@ def test_todo_duration_maintains_backward_compatibility():
110106
def test_todo_duration_edge_case_only_dtstart():
111107
"""Test Todo with only `DTSTART` (no `DUE` or `DURATION`)."""
112108
todo = Todo()
113-
todo.add("UID", "test-only-start")
109+
todo.add('UID', 'test-only-start')
114110
todo.start = datetime(2026, 3, 19, 12, 0)
115-
111+
116112
# This should use the fallback logic from the end property
117113
# which returns start + 1 day for date, or just start for datetime
118114
assert todo.duration == timedelta(0) # end defaults to start for datetime
@@ -131,26 +127,23 @@ def test_issue_867_exact_reproduction():
131127

132128
# This assertion was failing before the fix
133129
assert my_task.duration == timedelta(days=5)
134-
130+
135131
# Verify the todo has the expected properties
136-
assert my_task.get("UID") == "taskwithoutdtstart"
137-
assert (
138-
my_task.get("SUMMARY")
139-
== "This is a task that is expected to take five days to complete"
140-
)
141-
assert "DURATION" in my_task
142-
assert "DTSTART" not in my_task # Confirming no DTSTART
132+
assert my_task.get('UID') == 'taskwithoutdtstart'
133+
assert my_task.get('SUMMARY') == 'This is a task that is expected to take five days to complete'
134+
assert 'DURATION' in my_task
135+
assert 'DTSTART' not in my_task # Confirming no DTSTART
143136

144137

145138
def test_todo_duration_preserves_property_access():
146139
"""Test that direct property access still works as expected."""
147140
todo = Todo()
148-
todo.add("UID", "test-property-access")
149-
todo.add("DURATION", timedelta(hours=2))
150-
141+
todo.add('UID', 'test-property-access')
142+
todo.add('DURATION', timedelta(hours=2))
143+
151144
# Direct property access should still work
152-
assert todo["DURATION"].dt == timedelta(hours=2)
153-
145+
assert todo['DURATION'].dt == timedelta(hours=2)
146+
154147
# And our computed property should match
155148
assert todo.duration == timedelta(hours=2)
156149

@@ -159,21 +152,21 @@ def test_todo_duration_preserves_property_access():
159152
def test_event_duration_prefers_duration_property():
160153
"""Test that Event.duration also prefers `DURATION` property over calculated duration."""
161154
event = Event()
162-
event.add("UID", "test-event-duration")
155+
event.add('UID', 'test-event-duration')
163156
event.start = datetime(2026, 3, 19, 12, 0)
164157
event.end = datetime(2026, 3, 19, 15, 0) # This would be 3 hours
165-
event.add("DURATION", timedelta(hours=2)) # But DURATION says 2 hours
166-
158+
event.add('DURATION', timedelta(hours=2)) # But DURATION says 2 hours
159+
167160
# Should return DURATION property, not calculated value
168161
assert event.duration == timedelta(hours=2)
169162

170163

171164
def test_event_duration_calculated_fallback():
172165
"""Test that Event.duration falls back to calculated duration when no `DURATION` property."""
173166
event = Event()
174-
event.add("UID", "test-event-calculated")
167+
event.add('UID', 'test-event-calculated')
175168
event.start = datetime(2026, 3, 19, 12, 0)
176169
event.end = datetime(2026, 3, 19, 14, 30)
177-
170+
178171
# Should calculate duration from start and end (no DURATION property)
179-
assert event.duration == timedelta(hours=2, minutes=30)
172+
assert event.duration == timedelta(hours=2, minutes=30)

0 commit comments

Comments
 (0)