Skip to content

Commit 3d2eff5

Browse files
committed
Deprecate string file paths in Component.from_ical
Right now, if you pass from_ical a single-line string that happens to be the path of an existing file, it quietly reads that file off disk. That's surprising and a little risky, so warn when it happens and line the behaviour up for removal in icalendar 8. To read a file, pass a Path. Refs #1362
1 parent 02d750d commit 3d2eff5

3 files changed

Lines changed: 34 additions & 4 deletions

File tree

news/1362.removal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
``Component.from_ical`` now warns when you hand it a file path as a string. It still reads the file as before, but this is deprecated: in icalendar 8 a string will always be parsed as calendar data, never treated as a path. If you want to read from a file, pass a :class:`pathlib.Path`. @uwezkhan

src/icalendar/cal/component.py

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from __future__ import annotations
44

55
import json
6+
import warnings
67
from copy import deepcopy
78
from datetime import date, datetime, time, timedelta, timezone
89
from pathlib import Path
@@ -22,7 +23,11 @@
2223
)
2324
from icalendar.cal.component_factory import ComponentFactory
2425
from icalendar.caselessdict import CaselessDict
25-
from icalendar.error import InvalidCalendar, JCalParsingError
26+
from icalendar.error import (
27+
FeatureWillBeRemovedInFutureVersion,
28+
InvalidCalendar,
29+
JCalParsingError,
30+
)
2631
from icalendar.parser import (
2732
Contentline,
2833
Contentlines,
@@ -525,7 +530,12 @@ def from_ical(
525530
526531
Parameters:
527532
st: iCalendar data as bytes or string, or a path to an iCalendar file as
528-
:class:`pathlib.Path` or string.
533+
:class:`pathlib.Path`.
534+
535+
.. deprecated:: 7.2.0
536+
Passing a file path as a :class:`str` is deprecated and will be
537+
removed in icalendar 8. A ``str`` will then always be parsed as
538+
iCalendar data. Pass a :class:`pathlib.Path` to read from a file.
529539
multiple: If ``True``, returns list. If ``False``, returns single component.
530540
531541
Returns:
@@ -543,6 +553,14 @@ def from_ical(
543553
except OSError:
544554
is_file = False
545555
if is_file:
556+
warnings.warn(
557+
"Passing a file path as a string to from_ical() is deprecated "
558+
"and will be removed in icalendar 8. A string will then always "
559+
"be parsed as iCalendar data. Pass a pathlib.Path instead to "
560+
"read iCalendar data from a file.",
561+
FeatureWillBeRemovedInFutureVersion,
562+
stacklevel=2,
563+
)
546564
st = path.read_bytes()
547565
parser = cls._get_ical_parser(st)
548566
components = parser.parse()

src/icalendar/tests/test_issue_756_read_calendar_from_file.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@
33
See issue: https://github.com/collective/icalendar/issues/756
44
"""
55

6+
import warnings
67
from pathlib import Path
78

89
import pytest
910

1011
from icalendar import Calendar
1112
from icalendar.cal.examples import get_example
13+
from icalendar.error import FeatureWillBeRemovedInFutureVersion
1214

1315

1416
@pytest.fixture
@@ -28,10 +30,19 @@ def test_reading_cal_from_path(dummy_cal):
2830
assert actual_cal.to_ical() == expected_cal.to_ical()
2931

3032

33+
def test_reading_cal_from_path_does_not_warn(dummy_cal):
34+
"""Reading from a :class:`~pathlib.Path` is the explicit API and must not warn."""
35+
_, path = dummy_cal
36+
with warnings.catch_warnings():
37+
warnings.simplefilter("error", FeatureWillBeRemovedInFutureVersion)
38+
Calendar.from_ical(path)
39+
40+
3141
def test_reading_cal_from_string_path(dummy_cal):
32-
"""Test reading a calendar from a valid string path."""
42+
"""Reading from a string path still works but is deprecated (see issue #1362)."""
3343
expected_cal, path = dummy_cal
34-
actual_cal = Calendar.from_ical(str(path))
44+
with pytest.warns(FeatureWillBeRemovedInFutureVersion):
45+
actual_cal = Calendar.from_ical(str(path))
3546

3647
assert actual_cal.to_ical() == expected_cal.to_ical()
3748

0 commit comments

Comments
 (0)