Modify doctests and add more examples

pull/623/head
Nicco Kunzmann 2024-06-19 11:25:02 +01:00
rodzic 4583615b7e
commit 96b4e76cb5
3 zmienionych plików z 130 dodań i 16 usunięć

Wyświetl plik

@ -45,15 +45,23 @@ files.
Quick Guide
-----------
``icalendar`` enables you to **create**, **inspect** and **modify**
calendaring information with Python.
To **install** the package, run::
pip install icalendar
Inspect Files
~~~~~~~~~~~~~
You can open an ``.ics`` file and see all the events::
>>> import icalendar
>>> path_to_ics_file = "src/icalendar/tests/calendars/example.ics"
>>> with open(path_to_ics_file) as f:
>>> from pathlib import Path
>>> ics_path = Path("src/icalendar/tests/calendars/example.ics")
>>> with ics_path.open() as f:
... calendar = icalendar.Calendar.from_ical(f.read())
>>> for event in calendar.walk('VEVENT'):
... print(event.get("SUMMARY"))
@ -61,7 +69,74 @@ You can open an ``.ics`` file and see all the events::
Orthodox Christmas
International Women's Day
Using this package, you can also create calendars from scratch or edit existing ones.
Modify Content
~~~~~~~~~~~~~~
Such a calendar can then be edited and saved again.
.. code:: python
>>> calendar["X-WR-CALNAME"] = "My Modified Calendar" # modify
>>> print(calendar.to_ical()[:129]) # save modification
BEGIN:VCALENDAR
VERSION:2.0
PRODID:collective/icalendar
CALSCALE:GREGORIAN
METHOD:PUBLISH
X-WR-CALNAME:My Modified Calendar
Create Events, TODOs, Journals, Alarms, ...
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
``icalendar`` supports the creation and parsing of all kinds of objects
in the standard.
.. code:: python
>>> icalendar.Event() # events
VEVENT({})
>>> icalendar.FreeBusy() # free/busy times
VFREEBUSY({})
>>> icalendar.Todo() # Todo list entries
VTODO({})
>>> icalendar.Alarm() # Alarms e.g. for events
VALARM({})
>>> icalendar.Journal() # Journal entries
VJOURNAL({})
Have a look at `more examples
<https://icalendar.readthedocs.io/en/latest/usage.html>`_.
Use Timezones of your choice
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
With ``icalendar``, you can localize your events to take place in different
timezones.
``zoneinfo``, ``dateutil.tz`` and ``pytz`` are compatible with ``icalendar``.
This example creates an event that uses all of the timezone implementations
with the same result:
.. code:: python
>>> import pytz, zoneinfo, dateutil.tz # timezone libraries
>>> import datetime, icalendar
>>> e = icalendar.Event()
>>> tz = dateutil.tz.tzstr("Europe/London")
>>> e["X-DT-DATEUTIL"] = icalendar.vDatetime(datetime.datetime(2024, 6, 19, 10, 1, tzinfo=tz))
>>> tz = pytz.timezone("Europe/London")
>>> e["X-DT-USE-PYTZ"] = icalendar.vDatetime(datetime.datetime(2024, 6, 19, 10, 1, tzinfo=tz))
>>> tz = zoneinfo.ZoneInfo("Europe/London")
>>> e["X-DT-ZONEINFO"] = icalendar.vDatetime(datetime.datetime(2024, 6, 19, 10, 1, tzinfo=tz))
>>> print(e.to_ical()) # the libraries yield the same result
BEGIN:VEVENT
X-DT-DATEUTIL;TZID=Europe/London:20240619T100100
X-DT-USE-PYTZ;TZID=Europe/London:20240619T100100
X-DT-ZONEINFO;TZID=Europe/London:20240619T100100
END:VEVENT
Versions and Compatibility
--------------------------
@ -70,20 +145,30 @@ Versions and Compatibility
long-term compatibility with projects conflicts partially with providing and using the features that
the latest Python versions bring.
Since we pour more `effort into maintaining and developing icalendar <https://github.com/collective/icalendar/discussions/360>`__,
this is an overview of the versions:
Volunteers pour `effort into maintaining and developing icalendar
<https://github.com/collective/icalendar/discussions/360>`__.
Below, you can find an overview of the versions and how we maintain them.
Version 6
~~~~~~~~~
Version 6 of ``icalendar`` switches the timezone implementation to ``zoneinfo``.
This only affects you if you parse ``icalendar`` objects with ``from_ical()``.
The functionality is extended and is tested since 6.0.0 with both timezone
implementations: ``pytz`` and ``zoneinfo``.
By default and since 6.0.0, ``zoneinfo`` timezones are created.
.. code:: python
>>> dt = icalendar.Calendar.example("timezoned").walk("VEVENT")[0]["DTSTART"].dt
>>> dt.tzinfo
ZoneInfo(key='Europe/Vienna')
If you would like to continue to use ``pytz`` and receive the latest updates, you
can switch back:
If you would like to continue to receive ``pytz`` timezones in as parse results,
you can receive all the latest updates, and switch back to version 5.x behavior:
.. code:: python
>>> icalendar.use_pytz()
>>> dt = icalendar.Calendar.example("timezoned").walk("VEVENT")[0]["DTSTART"].dt

Wyświetl plik

@ -2,11 +2,6 @@ try:
from backports import zoneinfo
except ImportError:
import zoneinfo
# we make it nicer for doctests
class ZoneInfo(zoneinfo.ZoneInfo):
def __repr__(self):
return f"ZoneInfo(key={repr(self.key)})"
zoneinfo.ZoneInfo = ZoneInfo
import pytest
import icalendar
import pytz
@ -17,6 +12,7 @@ from icalendar.timezone import tzp as _tzp
from icalendar.timezone import TZP
from pathlib import Path
import itertools
import sys
class DataSource:
@ -240,3 +236,26 @@ def pytest_generate_tests(metafunc):
tzp_names.remove("zoneinfo")
assert tzp_names, "Use pytz_only or zoneinfo_only but not both!"
metafunc.parametrize("tzp_name", tzp_names, scope="module")
class DoctestZoneInfo(zoneinfo.ZoneInfo):
"""Constent ZoneInfo representation for tests."""
def __repr__(self):
return f"ZoneInfo(key={repr(self.key)})"
def test_print(obj):
"""doctest print"""
if isinstance(obj, bytes):
obj = obj.decode("UTF-8")
print(str(obj).strip().replace("\r\n", "\n").replace("\r", "\n"))
@pytest.fixture()
def env_for_doctest(monkeypatch):
"""Modify the environment to make doctests run."""
monkeypatch.setitem(sys.modules, "zoneinfo", zoneinfo)
monkeypatch.setattr(zoneinfo, "ZoneInfo", DoctestZoneInfo)
from icalendar.timezone.zoneinfo import ZONEINFO
monkeypatch.setattr(ZONEINFO, "utc", zoneinfo.ZoneInfo("UTC"))
return {"print": test_print}

Wyświetl plik

@ -60,9 +60,19 @@ except FileNotFoundError:
def test_files_is_included(filename):
assert any(path.endswith(filename) for path in DOCUMENT_PATHS)
@pytest.mark.parametrize("document", DOCUMENT_PATHS)
def test_documentation_file(document, zoneinfo_only):
"""This test runs doctest on a documentation file."""
test_result = doctest.testfile(document, module_relative=False)
@pytest.mark.parametrize("document", DOCUMENT_PATHS)
def test_documentation_file(document, zoneinfo_only, env_for_doctest):
"""This test runs doctest on a documentation file.
functions are also replaced to work.
"""
test_result = doctest.testfile(document, module_relative=False, globs=env_for_doctest)
assert test_result.failed == 0, f"{test_result.failed} errors in {os.path.basename(document)}"
def test_can_import_zoneinfo(env_for_doctest):
"""Allow importing zoneinfo for tests."""
import pytz
import zoneinfo
from dateutil import tz