kopia lustrzana https://github.com/collective/icalendar
Merge branch 'master' into doctest
commit
dc2c280f12
|
@ -0,0 +1,39 @@
|
|||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: "[BUG] "
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!-- This template is there to guide you and help us. If you can not complete everything here, that is fine. -->
|
||||
## Describe the bug
|
||||
<!-- A clear and concise description of what the bug is. -->
|
||||
|
||||
## To Reproduce
|
||||
<!-- Please add the neccesary code here to reproduce the problem on your machine. -->
|
||||
|
||||
```
|
||||
import icalendar
|
||||
...
|
||||
```
|
||||
Output:
|
||||
<!-- If applicable, add logs or error outputs to help explain your problem. -->
|
||||
```
|
||||
|
||||
```
|
||||
|
||||
## Expected behavior**
|
||||
<!-- A clear and concise description of what you expected to happen. -->
|
||||
|
||||
## Environment
|
||||
<!-- please complete the following information: -->
|
||||
- [ ] OS: <!-- e.g. Ubuntu 22 or Windows 10 -->
|
||||
- [ ] Python version: <!-- e.g. Python 3.10 -->
|
||||
- [ ] `icalendar` version: <!-- python3 -c 'import icalendar; print(icalendar.__version__)' -->
|
||||
|
||||
## Additional context
|
||||
<!-- Add any other context about the problem here, related issues and pull requests. -->
|
||||
- [ ] I tested it with the latest version `pip3 install https://github.com/collective/icalendar.git`
|
||||
- [ ] I attached the ICS source file or there is no ICS source file
|
|
@ -0,0 +1,18 @@
|
|||
---
|
||||
name: empty issue
|
||||
about: This is an unstructured issue template to use with issues that are not described,
|
||||
yet.
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!-- This is an unstructured issue template to use if you do not need guidance.
|
||||
Here are some hints though:
|
||||
- add ICS example files if you can
|
||||
- show code if possible
|
||||
- show error output or output that differs from what you expect
|
||||
- it is nice to connect to the value of this issue
|
||||
Thanks for taking your time to report this!
|
||||
-->
|
|
@ -2,14 +2,17 @@ name: tests
|
|||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
branches:
|
||||
- main
|
||||
tags:
|
||||
- v*
|
||||
pull_request:
|
||||
schedule:
|
||||
- cron: '14 7 * * 0' # run once a week on Sunday
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
run-tests:
|
||||
strategy:
|
||||
matrix:
|
||||
config:
|
||||
|
@ -20,6 +23,7 @@ jobs:
|
|||
- ["3.10", "py310"]
|
||||
- ["pypy-3.9", "pypy3"]
|
||||
- ["3.10", "docs"]
|
||||
- ["3.10", "plone"]
|
||||
- ["3.11.0-rc.1", "py311"]
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
@ -50,3 +54,61 @@ jobs:
|
|||
coveralls --service=github
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
deploy-tag-to-pypi:
|
||||
# only deploy on tags, see https://stackoverflow.com/a/58478262/1320237
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
needs:
|
||||
- run-tests
|
||||
runs-on: ubuntu-latest
|
||||
# This environment stores the TWINE_USERNAME and TWINE_PASSWORD
|
||||
# see https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment
|
||||
environment:
|
||||
name: PyPI
|
||||
url: https://pypi.org/project/icalendar/
|
||||
# after using the environment, we need to make the secrets available
|
||||
# see https://docs.github.com/en/actions/security-guides/encrypted-secrets#example-using-bash
|
||||
env:
|
||||
TWINE_USERNAME: ${{ secrets.TWINE_USERNAME }}
|
||||
TWINE_PASSWORD: ${{ secrets.TWINE_PASSWORD }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: "3.9"
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install wheel twine
|
||||
- name: Check the tag
|
||||
run: |
|
||||
PACKAGE_VERSION=`python setup.py --version`
|
||||
TAG_NAME=v$PACKAGE_VERSION
|
||||
echo "Package version $PACKAGE_VERSION with possible tag name $TAG_NAME on $GITHUB_REF_NAME"
|
||||
# test that the tag represents the version
|
||||
# see https://docs.github.com/en/actions/learn-github-actions/environment-variables
|
||||
if [ "$TAG_NAME" != "$GITHUB_REF_NAME" ]; then
|
||||
echo "ERROR: This tag is for the wrong version. Got \"$GITHUB_REF_NAME\" expected \"$TAG_NAME\"."
|
||||
exit 1
|
||||
fi
|
||||
- name: remove old files
|
||||
run: rm -rf dist/*
|
||||
- name: build distribution files
|
||||
run: python setup.py bdist_wheel sdist
|
||||
- name: deploy to pypi
|
||||
run: |
|
||||
# You will have to set the variables TWINE_USERNAME and TWINE_PASSWORD
|
||||
# You can use a token specific to your project by setting the user name to
|
||||
# __token__ and the password to the token given to you by the PyPI project.
|
||||
# sources:
|
||||
# - https://shambu2k.hashnode.dev/gitlab-to-pypi
|
||||
# - http://blog.octomy.org/2020/11/deploying-python-pacakges-to-pypi-using.html?m=1
|
||||
if [ -z "$TWINE_USERNAME" ]; then
|
||||
echo "WARNING: TWINE_USERNAME not set!"
|
||||
fi
|
||||
if [ -z "$TWINE_PASSWORD" ]; then
|
||||
echo "WARNING: TWINE_PASSWORD not set!"
|
||||
fi
|
||||
twine check dist/*
|
||||
twine upload dist/*
|
||||
|
|
27
CHANGES.rst
27
CHANGES.rst
|
@ -1,21 +1,42 @@
|
|||
Changelog
|
||||
=========
|
||||
|
||||
5.0.1 (unreleased)
|
||||
------------------
|
||||
|
||||
5.0.0a2 (unreleased)
|
||||
--------------------
|
||||
Minor changes:
|
||||
|
||||
- fixed setuptools deprecation warnings [mgorny]
|
||||
|
||||
Breaking changes:
|
||||
|
||||
- ...
|
||||
|
||||
New features:
|
||||
|
||||
- ...
|
||||
|
||||
Bug fixes:
|
||||
|
||||
- ...
|
||||
|
||||
5.0.0 (2022-10-17)
|
||||
------------------
|
||||
|
||||
Minor changes:
|
||||
|
||||
- removed deprecated test checks [tuergeist]
|
||||
- Fix: cli does not support DURATION #354 [mamico]
|
||||
- Add changelog and contributing to readthedocs documentation #428 [peleccom]
|
||||
- fixed small typos #323 [rohnsha0]
|
||||
- unittest to parametrized pytest refactoring [jacadzaca]
|
||||
|
||||
Breaking changes:
|
||||
|
||||
- Require Python 3.7 as minimum Python version. [maurits] [niccokunzmann]
|
||||
- icalenar now takes a ics file directly as an input
|
||||
- icalendar's output is different
|
||||
- Drop Support for Python 3.6. Versions 3.7 - 3.11 are supported and tested.
|
||||
|
||||
New features:
|
||||
|
||||
|
@ -36,6 +57,8 @@ Bug fixes:
|
|||
Ref: #338
|
||||
Fixes: #335
|
||||
[tobixen]
|
||||
- add ``__eq__`` to ``icalendar.prop.vDDDTypes`` #391 [jacadzaca]
|
||||
- Refactor deprecated unittest aliases for Python 3.11 compatibility #330 [tirkarthi]
|
||||
|
||||
5.0.0a1 (2022-07-11)
|
||||
--------------------
|
||||
|
|
|
@ -63,6 +63,7 @@ icalendar contributors
|
|||
- jacadzaca <vitouejj@gmail.com>
|
||||
- Mauro Amico <mauro.amico@gmail.com>
|
||||
- Alexander Pitkin <peleccom@gmail.com>
|
||||
- Michał Górny <mgorny@gentoo.org>
|
||||
|
||||
Find out who contributed::
|
||||
|
||||
|
|
|
@ -25,6 +25,9 @@ Maintainers need this:
|
|||
- ``Maintainer`` access to the `Read The Docs project <https://readthedocs.org/projects/icalendar/>`_.
|
||||
This can be given by existing maintainers listed on the project's page.
|
||||
TODO: link to the settings
|
||||
- ``PyPI environment access for GitHub Actions`` grant new releases from tags.
|
||||
This access can be granted in `Settings → Environments → PyPI <https://github.com/collective/icalendar/settings/environments/674266024/edit>`__
|
||||
by adding the GitHub username to the list of "Required Reviewers".
|
||||
|
||||
|
||||
Contributors
|
||||
|
@ -42,37 +45,79 @@ Nobody should merge their own pull requests.
|
|||
If you like to review or merge pull requests of other people and you have shown that you know how to contribute,
|
||||
you can ask for becoming a contributor or a maintainer asks you if you would like to become one.
|
||||
|
||||
|
||||
New Releases
|
||||
------------
|
||||
|
||||
This explains how to create a new release on PyPI.
|
||||
You will need write access to the `PyPI project`_.
|
||||
This explains how to create a new release on `PyPI <https://pypi.org/project/icalendar/>`_.
|
||||
|
||||
1. Check that the ``CHANGES.rst`` is up to date with the latest merges and the version you want to release is correctly named.
|
||||
Since contributors and maintainers have write access the the repository, they can start the release process.
|
||||
However, only people with ``PyPI environment access for GitHub Actions`` can approve an automated release to PyPI.
|
||||
|
||||
|
||||
1. Check that the ``CHANGES.rst`` is up to date with the `latest merged pull requests <https://github.com/collective/icalendar/pulls?q=is%3Apr+is%3Amerged>`__
|
||||
and the version you want to release is correctly named.
|
||||
2. Change the ``__version__`` variable in
|
||||
|
||||
- the ``src/icalendar/__init__.py`` file and
|
||||
- in the ``docs/usage.rst`` file (look for ``icalendar.__version__``)
|
||||
3. Create a commit on the ``master`` branch to release this version.
|
||||
3. Create a commit on the ``release-5.0.0`` branch (or equivalent) to release this version.
|
||||
|
||||
.. code-block:: bash
|
||||
.. code-block:: bash
|
||||
|
||||
git checkout master
|
||||
git commit -am"version 5.0.0a2"
|
||||
4. Push the commit and see if the `CI-tests <https://github.com/collective/icalendar/actions?query=branch%3Amaster>`__ are running on it.
|
||||
git checkout master
|
||||
git pull
|
||||
git checkout -b release master
|
||||
git add CHANGES.rst src/icalendar/__init__.py
|
||||
git commit -m"version 5.0.0"
|
||||
|
||||
.. code-block:: bash
|
||||
4. Push the commit and `create a pull request <https://github.com/collective/icalendar/compare?expand=1>`__
|
||||
Here is an `example pull request #457 <https://github.com/collective/icalendar/pull/457>`__.
|
||||
|
||||
git push
|
||||
5. Create a tag for the release and see if the `CI-tests <https://github.com/collective/icalendar/actions>`__ are running.
|
||||
.. code-block:: bash
|
||||
|
||||
.. code-block:: bash
|
||||
git push -u origin release-5.0.0
|
||||
|
||||
git tag v5.0.0a2
|
||||
git push origin v5.0.0a2
|
||||
6. TODO: how to release new version to PyPI.
|
||||
5. See if the `CI-tests <https://github.com/collective/icalendar/actions>`_ are running on the pull request.
|
||||
If they are not running, no new release can be issued.
|
||||
If the tests are running, merge the pull request.
|
||||
6. Create a tag for the release and see if the `CI-tests`_ are running.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
git checkout master
|
||||
git pull
|
||||
git tag v5.0.0
|
||||
git push upstream v5.0.0 # could be origin or whatever reference
|
||||
|
||||
7. Once the tag is pushed and its `CI-tests`_ are passing, maintainers will get an e-mail::
|
||||
|
||||
Subject: Deployment review in collective/icalendar
|
||||
|
||||
tests: PyPI is waiting for your review
|
||||
|
||||
8. If the release is approved by a maintainer. It will be pushed to `PyPI`_.
|
||||
If that happens, notify the issues that were fixed about this release.
|
||||
9. Copy this to the start of ``CHANGES.rst`` and create a new commit with this on the ``master`` branch::
|
||||
|
||||
5.0.1 (unreleased)
|
||||
------------------
|
||||
|
||||
Minor changes:
|
||||
|
||||
- ...
|
||||
|
||||
Breaking changes:
|
||||
|
||||
- ...
|
||||
|
||||
New features:
|
||||
|
||||
- ...
|
||||
|
||||
Bug fixes:
|
||||
|
||||
- ...
|
||||
|
||||
|
||||
Links
|
||||
-----
|
||||
|
@ -82,6 +127,7 @@ This section contains useful links for maintainers and contributors:
|
|||
- `Future of icalendar, looking for maintainer #360 <https://github.com/collective/icalendar/discussions/360>`__
|
||||
- `Team icalendar-admin <https://github.com/orgs/collective/teams/icalendar-admin>`__
|
||||
- `Team icalendar-contributor <https://github.com/orgs/collective/teams/icalendar-contributor>`__
|
||||
- `Comment on the Plone tests running with icalendar <https://github.com/collective/icalendar/pull/447#issuecomment-1277643634>`__
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ create-wheel = yes
|
|||
universal = 1
|
||||
|
||||
[metadata]
|
||||
license_file = LICENSE.rst
|
||||
license_files = LICENSE.rst
|
||||
|
||||
[tool:pytest]
|
||||
norecursedirs = .* env* docs *.egg src/icalendar/tests/hypothesis
|
||||
|
|
3
setup.py
3
setup.py
|
@ -42,6 +42,7 @@ setuptools.setup(
|
|||
'Programming Language :: Python :: 3.8',
|
||||
'Programming Language :: Python :: 3.9',
|
||||
'Programming Language :: Python :: 3.10',
|
||||
'Programming Language :: Python :: 3.11',
|
||||
'Programming Language :: Python :: Implementation :: CPython',
|
||||
'Programming Language :: Python :: Implementation :: PyPy',
|
||||
],
|
||||
|
@ -51,7 +52,7 @@ setuptools.setup(
|
|||
author_email='plone-developers@lists.sourceforge.net',
|
||||
url='https://github.com/collective/icalendar',
|
||||
license='BSD',
|
||||
packages=setuptools.find_packages('src'),
|
||||
packages=setuptools.find_namespace_packages('src'),
|
||||
package_dir={'': 'src'},
|
||||
include_package_data=True,
|
||||
zip_safe=False,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
__version__ = '5.0.0a2.dev0'
|
||||
__version__ = '5.0.0'
|
||||
|
||||
from icalendar.cal import (
|
||||
Calendar,
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
BEGIN:VCALENDAR
|
||||
BEGIN:VEVENT
|
||||
SUMMARY:An Event with too many semicolons
|
||||
DTSTART;;VALUE=DATE-TIME:20140409T093000
|
||||
UID:abc
|
||||
END:VEVENT
|
||||
END:VCALENDAR
|
|
@ -0,0 +1,25 @@
|
|||
BEGIN:VCALENDAR
|
||||
X-SOURCE-URL:https://github.com/pimutils/khal/issues/152#issuecomment-387410353
|
||||
VERSION:2.0
|
||||
PRODID:-//PIMUTILS.ORG//NONSGML khal / icalendar //EN
|
||||
BEGIN:VEVENT
|
||||
SUMMARY:Event
|
||||
DTSTART;TZID=America/Chicago;VALUE=DATE-TIME:20180327T080000
|
||||
DTEND;TZID=America/Chicago;VALUE=DATE-TIME:20180327T090000
|
||||
DTSTAMP:20180323T200333Z
|
||||
RECURRENCE-ID;RANGE=THISANDFUTURE:20180327T130000Z
|
||||
SEQUENCE:10
|
||||
RDATE;TZID="Central Standard Time";VALUE=PERIOD:20180327T080000/20180327T0
|
||||
90000,20180403T080000/20180403T090000,20180410T080000/20180410T090000,2018
|
||||
0417T080000/20180417T090000,20180424T080000/20180424T090000,20180501T08000
|
||||
0/20180501T090000,20180508T080000/20180508T090000,20180515T080000/20180515
|
||||
T090000,20180522T080000/20180522T090000,20180529T080000/20180529T090000,20
|
||||
180605T080000/20180605T090000,20180612T080000/20180612T090000,20180619T080
|
||||
000/20180619T090000,20180626T080000/20180626T090000,20180703T080000/201807
|
||||
03T090000,20180710T080000/20180710T090000,20180717T080000/20180717T090000,
|
||||
20180724T080000/20180724T090000,20180731T080000/20180731T090000
|
||||
ATTENDEE;CN="XYZ";PARTSTAT=ACCEPTED;ROLE=CHAIR;RSVP=
|
||||
FALSE:mailto:xyz@xyz.com
|
||||
CLASS:PUBLIC
|
||||
END:VEVENT
|
||||
END:VCALENDAR
|
|
@ -0,0 +1,55 @@
|
|||
BEGIN:VCALENDAR
|
||||
X-SOURCE-URL:https://github.com/pimutils/khal/issues/152#issuecomment-933635248
|
||||
VERSION:2.0
|
||||
PRODID:-//PIMUTILS.ORG//NONSGML khal / icalendar //EN
|
||||
BEGIN:VTIMEZONE
|
||||
TZID:Western/Central Europe
|
||||
BEGIN:STANDARD
|
||||
DTSTART:19501029T020000
|
||||
RRULE:FREQ=YEARLY;BYMINUTE=0;BYHOUR=2;BYDAY=-1SU;BYMONTH=10
|
||||
TZOFFSETFROM:+0200
|
||||
TZOFFSETTO:+0100
|
||||
END:STANDARD
|
||||
BEGIN:DAYLIGHT
|
||||
DTSTART:19500326T020000
|
||||
RRULE:FREQ=YEARLY;BYMINUTE=0;BYHOUR=2;BYDAY=-1SU;BYMONTH=3
|
||||
TZOFFSETFROM:+0100
|
||||
TZOFFSETTO:+0200
|
||||
END:DAYLIGHT
|
||||
END:VTIMEZONE
|
||||
BEGIN:VEVENT
|
||||
SUMMARY:(omitted)
|
||||
DTSTART;TZID="Western/Central Europe";VALUE=DATE-TIME:20211101T160000
|
||||
DTEND;TZID="Western/Central Europe";VALUE=DATE-TIME:20211101T163000
|
||||
DTSTAMP:20211004T150245Z
|
||||
UID:BF5109494E67AAE20025875100566D31-Lotus_Notes_Generated
|
||||
RECURRENCE-ID;RANGE=THISANDFUTURE:20211101T150000Z
|
||||
SEQUENCE:0
|
||||
RDATE;TZID="Western/Central Europe";VALUE=PERIOD:20211101T160000/20211101T
|
||||
163000,20211206T160000/20211206T163000,20220103T160000/20220103T163000,202
|
||||
20207T160000/20220207T163000
|
||||
ATTENDEE;CN="(omitted)";PARTSTAT=ACCEPTED;ROLE=CHAIR;RSVP=FAL
|
||||
SE:mailto:omitted@example.com
|
||||
CLASS:PUBLIC
|
||||
TRANSP:OPAQUE
|
||||
X-LOTUS-APPTTYPE:3
|
||||
X-LOTUS-AUDIOVIDEOFLAGS:0
|
||||
X-LOTUS-BROADCAST:FALSE
|
||||
X-LOTUS-CHANGE-INST-DATES:20211101T150000Z\,20211206T150000Z\,20220103T150
|
||||
000Z\,20220207T150000Z
|
||||
X-LOTUS-CHILD-UID:567EFBAF6CBD07FC0025875100566D3B
|
||||
X-LOTUS-INITIAL-RDATES:20211101T150000Z\,20211206T150000Z\,20220103T150000
|
||||
Z\,20220207T150000Z
|
||||
X-LOTUS-LASTALL-RDATES;TZID="Western/Central Europe":20211101T160000\,2021
|
||||
1206T160000\,20220103T160000\,20220207T160000
|
||||
X-LOTUS-NOTESVERSION:2
|
||||
X-LOTUS-NOTICETYPE:I
|
||||
X-LOTUS-RECURID;RANGE=THISANDFUTURE:20211101T150000Z
|
||||
X-LOTUS-UPDATE-SEQ:2
|
||||
X-LOTUS-UPDATE-WISL:$W:1\;$O:1\;$M:1\;RequiredAttendees:1\;INetRequiredNam
|
||||
es:1\;AltRequiredNames:1\;StorageRequiredNames:1\;OptionalAttendees:1\;INe
|
||||
tOptionalNames:1\;AltOptionalNames:1\;StorageOptionalNames:1\;ApptUNIDURL:
|
||||
1\;STUnyteConferenceURL:1\;STUnyteConferenceID:1\;SametimeType:1\;WhiteBoa
|
||||
rdContent:1\;STRoomName:1\;$S:2\;$B:2\;$L:2\;$E:2\;$R:2
|
||||
END:VEVENT
|
||||
END:VCALENDAR
|
|
@ -0,0 +1,23 @@
|
|||
BEGIN:VCALENDAR
|
||||
BEGIN:VTIMEZONE
|
||||
TZID:(UTC-03:00) Brasília
|
||||
BEGIN:STANDARD
|
||||
TZNAME:Brasília standard
|
||||
DTSTART:16010101T235959
|
||||
TZOFFSETFROM:-0200
|
||||
TZOFFSETTO:-0300
|
||||
RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=3SA;BYMONTH=2
|
||||
END:STANDARD
|
||||
BEGIN:DAYLIGHT
|
||||
TZNAME:Brasília daylight
|
||||
DTSTART:16010101T235959
|
||||
TZOFFSETFROM:-0300
|
||||
TZOFFSETTO:-0200
|
||||
RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=2SA;BYMONTH=10
|
||||
END:DAYLIGHT
|
||||
END:VTIMEZONE
|
||||
BEGIN:VEVENT
|
||||
DTSTART;TZID="(UTC-03:00) Brasília":20170511T133000
|
||||
DTEND;TZID="(UTC-03:00) Brasília":20170511T140000
|
||||
END:VEVENT
|
||||
END:VCALENDAR
|
|
@ -1,5 +1,4 @@
|
|||
import os
|
||||
import logging
|
||||
import pytest
|
||||
import icalendar
|
||||
import pytz
|
||||
|
@ -10,7 +9,6 @@ try:
|
|||
except ModuleNotFoundError:
|
||||
from backports import zoneinfo
|
||||
|
||||
|
||||
class DataSource:
|
||||
'''A collection of parsed ICS elements (e.g calendars, timezones, events)'''
|
||||
def __init__(self, data_source_folder, parser):
|
||||
|
@ -24,7 +22,8 @@ class DataSource:
|
|||
with open(source_path, 'rb') as f:
|
||||
raw_ics = f.read()
|
||||
source = self._parser(raw_ics)
|
||||
source.raw_ics = raw_ics
|
||||
if not isinstance(source, list):
|
||||
source.raw_ics = raw_ics
|
||||
self.__dict__[attribute] = source
|
||||
return source
|
||||
|
||||
|
@ -34,10 +33,19 @@ class DataSource:
|
|||
def __repr__(self):
|
||||
return repr(self.__dict__)
|
||||
|
||||
@property
|
||||
def multiple(self):
|
||||
"""Return a list of all components parsed."""
|
||||
return self.__class__(self._data_source_folder, lambda data: self._parser(data, multiple=True))
|
||||
|
||||
HERE = os.path.dirname(__file__)
|
||||
CALENDARS_FOLDER = os.path.join(HERE, 'calendars')
|
||||
TIMEZONES_FOLDER = os.path.join(HERE, 'timezones')
|
||||
EVENTS_FOLDER = os.path.join(HERE, 'events')
|
||||
CALENDARS_FOLDER = os.path.join(HERE, 'calendars')
|
||||
|
||||
@pytest.fixture
|
||||
def calendars():
|
||||
return DataSource(CALENDARS_FOLDER, icalendar.Calendar.from_ical)
|
||||
|
||||
@pytest.fixture
|
||||
def timezones():
|
||||
|
@ -56,6 +64,10 @@ def events():
|
|||
def utc(request):
|
||||
return request.param
|
||||
|
||||
@pytest.fixture
|
||||
def calendars():
|
||||
return DataSource(CALENDARS_FOLDER, icalendar.Calendar.from_ical)
|
||||
@pytest.fixture(params=[
|
||||
lambda dt, tzname: pytz.timezone(tzname).localize(dt),
|
||||
lambda dt, tzname: dt.replace(tzinfo=tz.gettz(tzname)),
|
||||
lambda dt, tzname: dt.replace(tzinfo=zoneinfo.ZoneInfo(tzname))
|
||||
])
|
||||
def in_timezone(request):
|
||||
return request.param
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
BEGIN:VEVENT
|
||||
SUMMARY:RDATE period
|
||||
DTSTART:19961230T020000Z
|
||||
DTEND:19961230T060000Z
|
||||
UID:rdate_period
|
||||
RDATE;VALUE=PERIOD:19970101T180000Z/19970102T070000Z
|
||||
END:VEVENT
|
|
@ -0,0 +1,8 @@
|
|||
BEGIN:VEVENT
|
||||
SUMMARY:RDATE period
|
||||
DTSTART:19961230T020000Z
|
||||
DTEND:19961230T060000Z
|
||||
UID:rdate_period
|
||||
RDATE;VALUE=PERIOD:19970101T180000Z/19970102T070000Z,19970109T180000Z/PT5H
|
||||
30M
|
||||
END:VEVENT
|
|
@ -1,78 +0,0 @@
|
|||
BEGIN:VCALENDAR
|
||||
VERSION:2.0
|
||||
PRODID:-//Meetup//RemoteApi//EN
|
||||
CALSCALE:GREGORIAN
|
||||
METHOD:PUBLISH
|
||||
X-ORIGINAL-URL:http://www.meetup.com/DevOpsDC/events/ical/DevOpsDC/
|
||||
X-WR-CALNAME:Events - DevOpsDC
|
||||
BEGIN:VTIMEZONE
|
||||
TZID:America/New_York
|
||||
TZURL:http://tzurl.org/zoneinfo-outlook/America/New_York
|
||||
X-LIC-LOCATION:America/New_York
|
||||
BEGIN:DAYLIGHT
|
||||
TZOFFSETFROM:-0500
|
||||
TZOFFSETTO:-0400
|
||||
TZNAME:EDT
|
||||
DTSTART:19700308T020000
|
||||
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
|
||||
END:DAYLIGHT
|
||||
BEGIN:STANDARD
|
||||
TZOFFSETFROM:-0400
|
||||
TZOFFSETTO:-0500
|
||||
TZNAME:EST
|
||||
DTSTART:19701101T020000
|
||||
RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
|
||||
END:STANDARD
|
||||
END:VTIMEZONE
|
||||
BEGIN:VEVENT
|
||||
DTSTAMP:20120605T003759Z
|
||||
DTSTART;TZID=America/New_York:20120712T183000
|
||||
DTEND;TZID=America/New_York:20120712T213000
|
||||
STATUS:CONFIRMED
|
||||
SUMMARY:DevOps DC Meetup
|
||||
DESCRIPTION:DevOpsDC\nThursday\, July 12 at 6:30 PM\n\nThis will be a joi
|
||||
nt meetup / hack night with the DC jQuery Users Group. The idea behind
|
||||
the hack night: Small teams consisting of at least 1 member...\n\nDeta
|
||||
ils: http://www.meetup.com/DevOpsDC/events/47635522/
|
||||
CLASS:PUBLIC
|
||||
CREATED:20120111T120339Z
|
||||
GEO:38.90;-77.01
|
||||
LOCATION:Fathom Creative\, Inc. (1333 14th Street Northwest\, Washington
|
||||
D.C.\, DC 20005)
|
||||
URL:http://www.meetup.com/DevOpsDC/events/47635522/
|
||||
LAST-MODIFIED:20120522T174406Z
|
||||
UID:event_qtkfrcyqkbnb@meetup.com
|
||||
END:VEVENT
|
||||
BEGIN:VEVENT
|
||||
DTSTAMP:20120605T003759Z
|
||||
DTSTART;TZID=America/New_York:20120911T183000
|
||||
DTEND;TZID=America/New_York:20120911T213000
|
||||
STATUS:CONFIRMED
|
||||
SUMMARY:DevOps DC Meetup
|
||||
DESCRIPTION:DevOpsDC\nTuesday\, September 11 at 6:30 PM\n\n \n\nDetails:
|
||||
http://www.meetup.com/DevOpsDC/events/47635532/
|
||||
CLASS:PUBLIC
|
||||
CREATED:20120111T120352Z
|
||||
GEO:38.90;-77.01
|
||||
LOCATION:CustomInk\, LLC (7902 Westpark Drive\, McLean\, VA 22102)
|
||||
URL:http://www.meetup.com/DevOpsDC/events/47635532/
|
||||
LAST-MODIFIED:20120316T202210Z
|
||||
UID:event_qtkfrcyqmbpb@meetup.com
|
||||
END:VEVENT
|
||||
BEGIN:VEVENT
|
||||
DTSTAMP:20120605T003759Z
|
||||
DTSTART;TZID=America/New_York:20121113T183000
|
||||
DTEND;TZID=America/New_York:20121113T213000
|
||||
STATUS:CONFIRMED
|
||||
SUMMARY:DevOps DC Meetup
|
||||
DESCRIPTION:DevOpsDC\nTuesday\, November 13 at 6:30 PM\n\n \n\nDetails: h
|
||||
ttp://www.meetup.com/DevOpsDC/events/47635552/
|
||||
CLASS:PUBLIC
|
||||
CREATED:20120111T120402Z
|
||||
GEO:38.90;-77.01
|
||||
LOCATION:CustomInk\, LLC (7902 Westpark Drive\, McLean\, VA 22102)
|
||||
URL:http://www.meetup.com/DevOpsDC/events/47635552/
|
||||
LAST-MODIFIED:20120316T202210Z
|
||||
UID:event_qtkfrcyqpbrb@meetup.com
|
||||
END:VEVENT
|
||||
END:VCALENDAR
|
|
@ -1,4 +1,5 @@
|
|||
import pytest
|
||||
import datetime
|
||||
|
||||
@pytest.mark.parametrize('field, expected_value', [
|
||||
('PRODID', '-//Plönë.org//NONSGML plone.app.event//EN'),
|
||||
|
@ -27,3 +28,41 @@ def test_events_parameter_unicoded(events):
|
|||
https://github.com/collective/icalendar/issues/101
|
||||
'''
|
||||
assert events.issue_101_icalendar_chokes_on_umlauts_in_organizer['ORGANIZER'].params['CN'] == 'acme, ädmin'
|
||||
|
||||
def test_parses_event_with_non_ascii_tzid_issue_237(calendars, in_timezone):
|
||||
"""Issue #237 - Fail to parse timezone with non-ascii TZID
|
||||
see https://github.com/collective/icalendar/issues/237
|
||||
"""
|
||||
start = calendars.issue_237_fail_to_parse_timezone_with_non_ascii_tzid.walk('VEVENT')[0].decoded('DTSTART')
|
||||
expected = in_timezone(datetime.datetime(2017, 5, 11, 13, 30), 'America/Sao_Paulo')
|
||||
assert not calendars.issue_237_fail_to_parse_timezone_with_non_ascii_tzid.errors
|
||||
assert start == expected
|
||||
|
||||
def test_parses_timezone_with_non_ascii_tzid_issue_237(timezones):
|
||||
"""Issue #237 - Fail to parse timezone with non-ascii TZID
|
||||
see https://github.com/collective/icalendar/issues/237
|
||||
"""
|
||||
assert timezones.issue_237_brazilia_standard['tzid'] == '(UTC-03:00) Brasília'
|
||||
|
||||
@pytest.mark.parametrize('timezone_name', ['standard', 'daylight'])
|
||||
def test_parses_timezone_with_non_ascii_tzname_issue_273(timezones, timezone_name):
|
||||
"""Issue #237 - Fail to parse timezone with non-ascii TZID
|
||||
see https://github.com/collective/icalendar/issues/237
|
||||
"""
|
||||
assert timezones.issue_237_brazilia_standard.walk(timezone_name)[0]['TZNAME'] == f'Brasília {timezone_name}'
|
||||
|
||||
def test_broken_property(calendars):
|
||||
"""
|
||||
Test if error messages are encoded properly.
|
||||
"""
|
||||
for event in calendars.broken_ical.walk('vevent'):
|
||||
assert len(event.errors) == 1, 'Not the right amount of errors.'
|
||||
error = event.errors[0][1]
|
||||
assert error.startswith('Content line could not be parsed into parts')
|
||||
|
||||
def test_apple_xlocation(calendars):
|
||||
"""
|
||||
Test if we support base64 encoded binary data in parameter values.
|
||||
"""
|
||||
for event in calendars.x_location.walk('vevent'):
|
||||
assert len(event.errors) == 0, 'Got too many errors'
|
||||
|
|
|
@ -42,47 +42,3 @@ class TestIssues(unittest.TestCase):
|
|||
event.to_ical(),
|
||||
icalendar.Event.from_ical(event.to_ical()).to_ical()
|
||||
)
|
||||
|
||||
def test_issue_237(self):
|
||||
"""Issue #237 - Fail to parse timezone with non-ascii TZID"""
|
||||
|
||||
ical_str = ['BEGIN:VCALENDAR',
|
||||
'BEGIN:VTIMEZONE',
|
||||
'TZID:(UTC-03:00) Brasília',
|
||||
'BEGIN:STANDARD',
|
||||
'TZNAME:Brasília standard',
|
||||
'DTSTART:16010101T235959',
|
||||
'TZOFFSETFROM:-0200',
|
||||
'TZOFFSETTO:-0300',
|
||||
'RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=3SA;BYMONTH=2',
|
||||
'END:STANDARD',
|
||||
'BEGIN:DAYLIGHT',
|
||||
'TZNAME:Brasília daylight',
|
||||
'DTSTART:16010101T235959',
|
||||
'TZOFFSETFROM:-0300',
|
||||
'TZOFFSETTO:-0200',
|
||||
'RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=2SA;BYMONTH=10',
|
||||
'END:DAYLIGHT',
|
||||
'END:VTIMEZONE',
|
||||
'BEGIN:VEVENT',
|
||||
'DTSTART;TZID=\"(UTC-03:00) Brasília\":20170511T133000',
|
||||
'DTEND;TZID=\"(UTC-03:00) Brasília\":20170511T140000',
|
||||
'END:VEVENT',
|
||||
'END:VCALENDAR',
|
||||
]
|
||||
|
||||
cal = icalendar.Calendar.from_ical('\r\n'.join(ical_str))
|
||||
self.assertEqual(cal.errors, [])
|
||||
|
||||
dtstart = cal.walk(name='VEVENT')[0].decoded("DTSTART")
|
||||
expected = pytz.timezone('America/Sao_Paulo').localize(datetime.datetime(2017, 5, 11, 13, 30))
|
||||
self.assertEqual(dtstart, expected)
|
||||
|
||||
try:
|
||||
expected_zone = '(UTC-03:00) Brasília'
|
||||
expected_tzname = 'Brasília standard'
|
||||
except UnicodeEncodeError:
|
||||
expected_zone = '(UTC-03:00) Brasília'.encode('ascii', 'replace')
|
||||
expected_tzname = 'Brasília standard'.encode('ascii', 'replace')
|
||||
self.assertEqual(dtstart.tzinfo.zone, expected_zone)
|
||||
self.assertEqual(dtstart.tzname(), expected_tzname)
|
||||
|
|
|
@ -291,34 +291,3 @@ class IcalendarTestCase (unittest.TestCase):
|
|||
'Max,Moller,"Rasmussen, Max"')
|
||||
|
||||
|
||||
class TestEncoding(unittest.TestCase):
|
||||
|
||||
def test_broken_property(self):
|
||||
"""
|
||||
Test if error messages are encode properly.
|
||||
"""
|
||||
broken_ical = textwrap.dedent("""
|
||||
BEGIN:VCALENDAR
|
||||
BEGIN:VEVENT
|
||||
SUMMARY:An Event with too many semicolons
|
||||
DTSTART;;VALUE=DATE-TIME:20140409T093000
|
||||
UID:abc
|
||||
END:VEVENT
|
||||
END:VCALENDAR
|
||||
""")
|
||||
cal = icalendar.Calendar.from_ical(broken_ical)
|
||||
for event in cal.walk('vevent'):
|
||||
self.assertEqual(len(event.errors), 1, 'Not the right amount of errors.')
|
||||
error = event.errors[0][1]
|
||||
self.assertTrue(error.startswith('Content line could not be parsed into parts'))
|
||||
|
||||
def test_apple_xlocation(self):
|
||||
"""
|
||||
Test if we support base64 encoded binary data in parameter values.
|
||||
"""
|
||||
directory = os.path.dirname(__file__)
|
||||
with open(os.path.join(directory, 'x_location.ics'), 'rb') as fp:
|
||||
data = fp.read()
|
||||
cal = icalendar.Calendar.from_ical(data)
|
||||
for event in cal.walk('vevent'):
|
||||
self.assertEqual(len(event.errors), 0, 'Got too many errors')
|
||||
|
|
|
@ -1,27 +1,15 @@
|
|||
"""An example with multiple VCALENDAR components"""
|
||||
from icalendar import Calendar
|
||||
from icalendar.prop import vText
|
||||
import unittest
|
||||
|
||||
import os
|
||||
|
||||
|
||||
class TestMultiple(unittest.TestCase):
|
||||
"""A example with multiple VCALENDAR components"""
|
||||
|
||||
def test_multiple(self):
|
||||
def test_multiple(calendars):
|
||||
"""Check opening multiple calendars."""
|
||||
|
||||
directory = os.path.dirname(__file__)
|
||||
with open(os.path.join(directory, 'multiple.ics'), 'rb') as fp:
|
||||
data = fp.read()
|
||||
cals = Calendar.from_ical(data, multiple=True)
|
||||
cals = calendars.multiple.multiple_calendar_components
|
||||
|
||||
self.assertEqual(len(cals), 2)
|
||||
self.assertSequenceEqual([comp.name for comp in cals[0].walk()],
|
||||
['VCALENDAR', 'VEVENT'])
|
||||
self.assertSequenceEqual([comp.name for comp in cals[1].walk()],
|
||||
['VCALENDAR', 'VEVENT', 'VEVENT'])
|
||||
|
||||
self.assertEqual(
|
||||
cals[0]['prodid'],
|
||||
vText('-//Mozilla.org/NONSGML Mozilla Calendar V1.0//EN')
|
||||
)
|
||||
assert len(cals) == 2
|
||||
assert [comp.name for comp in cals[0].walk()] == ['VCALENDAR', 'VEVENT']
|
||||
assert [comp.name for comp in cals[1].walk()] == ['VCALENDAR', 'VEVENT', 'VEVENT']
|
||||
assert cals[0]['prodid'] == vText('-//Mozilla.org/NONSGML Mozilla Calendar V1.0//EN')
|
||||
|
|
|
@ -15,9 +15,11 @@ from icalendar.parser import Contentline, Parameters
|
|||
# Nonstandard component inside other components, also has properties
|
||||
'issue_178_custom_component_inside_other',
|
||||
# Nonstandard component is able to contain other components
|
||||
'issue_178_custom_component_contains_other'])
|
||||
'issue_178_custom_component_contains_other',
|
||||
])
|
||||
def test_calendar_to_ical_is_inverse_of_from_ical(calendars, calendar_name):
|
||||
calendar = getattr(calendars, calendar_name)
|
||||
assert calendar.to_ical().splitlines() == calendar.raw_ics.splitlines()
|
||||
assert calendar.to_ical() == calendar.raw_ics
|
||||
|
||||
@pytest.mark.parametrize('raw_content_line, expected_output', [
|
||||
|
@ -74,10 +76,14 @@ def test_issue_157_removes_trailing_semicolon(events):
|
|||
# https://github.com/collective/icalendar/pull/100
|
||||
('issue_100_transformed_doctests_into_unittests'),
|
||||
('issue_184_broken_representation_of_period'),
|
||||
# PERIOD should be put back into shape
|
||||
'issue_156_RDATE_with_PERIOD',
|
||||
'issue_156_RDATE_with_PERIOD_list',
|
||||
])
|
||||
def test_event_to_ical_is_inverse_of_from_ical(events, event_name):
|
||||
"""Make sure that an event's ICS is equal to the ICS it was made from."""
|
||||
event = events[event_name]
|
||||
assert event.to_ical().splitlines() == event.raw_ics.splitlines()
|
||||
assert event.to_ical() == event.raw_ics
|
||||
|
||||
def test_decode_rrule_attribute_error_issue_70(events):
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
"""These tests make sure that we have some coverage on the usage of the PERIOD value type.
|
||||
|
||||
See
|
||||
- https://github.com/collective/icalendar/issues/156
|
||||
- https://github.com/pimutils/khal/issues/152#issuecomment-933635248
|
||||
"""
|
||||
import pytest
|
||||
import pytz
|
||||
from icalendar.prop import vDDDTypes
|
||||
|
||||
|
||||
@pytest.mark.parametrize("calname,tzname,index,period_string", [
|
||||
("issue_156_RDATE_with_PERIOD_TZID_khal_2", "Europe/Berlin", 0, "20211101T160000/20211101T163000"),
|
||||
("issue_156_RDATE_with_PERIOD_TZID_khal_2", "Europe/Berlin", 1, "20211206T160000/20211206T163000"),
|
||||
("issue_156_RDATE_with_PERIOD_TZID_khal_2", "Europe/Berlin", 2, "20220103T160000/20220103T163000"),
|
||||
("issue_156_RDATE_with_PERIOD_TZID_khal_2", "Europe/Berlin", 3, "20220207T160000/20220207T163000"),
|
||||
] + [
|
||||
("issue_156_RDATE_with_PERIOD_TZID_khal", "America/Chicago", i, period)
|
||||
for i, period in enumerate(("20180327T080000/20180327T0"
|
||||
"90000,20180403T080000/20180403T090000,20180410T080000/20180410T090000,2018"
|
||||
"0417T080000/20180417T090000,20180424T080000/20180424T090000,20180501T08000"
|
||||
"0/20180501T090000,20180508T080000/20180508T090000,20180515T080000/20180515"
|
||||
"T090000,20180522T080000/20180522T090000,20180529T080000/20180529T090000,20"
|
||||
"180605T080000/20180605T090000,20180612T080000/20180612T090000,20180619T080"
|
||||
"000/20180619T090000,20180626T080000/20180626T090000,20180703T080000/201807"
|
||||
"03T090000,20180710T080000/20180710T090000,20180717T080000/20180717T090000,"
|
||||
"20180724T080000/20180724T090000,20180731T080000/20180731T090000").split(","))
|
||||
])
|
||||
def test_issue_156_period_list_in_rdate(calendars, calname, tzname, index, period_string):
|
||||
"""Check items in a list of period values."""
|
||||
calendar = calendars[calname]
|
||||
rdate = calendar.walk("vevent")[0]["rdate"]
|
||||
period = rdate.dts[index]
|
||||
assert period.dt == vDDDTypes.from_ical(period_string, timezone=pytz.timezone(tzname))
|
||||
|
||||
|
||||
def test_duration_properly_parsed(events):
|
||||
"""This checks the duration PT5H30M."""
|
||||
start = vDDDTypes.from_ical("19970109T180000Z")
|
||||
duration = vDDDTypes.from_ical("PT5H30M")
|
||||
rdate = events.issue_156_RDATE_with_PERIOD_list["RDATE"]
|
||||
print(rdate)
|
||||
period = rdate.dts[1].dt
|
||||
print(dir(duration))
|
||||
assert period[0] == start
|
||||
assert period[1].days == 0
|
||||
assert period[1].seconds == (5 * 60 + 30) * 60
|
||||
assert period[1] == duration
|
|
@ -0,0 +1,17 @@
|
|||
BEGIN:VTIMEZONE
|
||||
TZID:(UTC-03:00) Brasília
|
||||
BEGIN:STANDARD
|
||||
TZNAME:Brasília standard
|
||||
DTSTART:16010101T235959
|
||||
TZOFFSETFROM:-0200
|
||||
TZOFFSETTO:-0300
|
||||
RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=3SA;BYMONTH=2
|
||||
END:STANDARD
|
||||
BEGIN:DAYLIGHT
|
||||
TZNAME:Brasília daylight
|
||||
DTSTART:16010101T235959
|
||||
TZOFFSETFROM:-0300
|
||||
TZOFFSETTO:-0200
|
||||
RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=2SA;BYMONTH=10
|
||||
END:DAYLIGHT
|
||||
END:VTIMEZONE
|
16
tox.ini
16
tox.ini
|
@ -1,6 +1,6 @@
|
|||
# to run for a specific environment, use ``tox -e ENVNAME``
|
||||
[tox]
|
||||
envlist = py37,py38,py39,py310,pypy3,docs
|
||||
envlist = py37,py38,py39,py310,pypy3,docs,plone
|
||||
# Note: the 'docs' env creates a 'build' directory which may interfere in strange ways
|
||||
# with the other environments. You might see this when you run the tests in parallel.
|
||||
# See https://github.com/collective/icalendar/pull/359#issuecomment-1214150269
|
||||
|
@ -23,3 +23,17 @@ changedir = docs
|
|||
allowlist_externals = make
|
||||
commands =
|
||||
make html
|
||||
|
||||
[testenv:plone]
|
||||
usedevelop = False
|
||||
install = False
|
||||
python = 3.10
|
||||
commands_pre =
|
||||
# Install Plone and explicitly the single package that uses icalendar, plus the test runner.
|
||||
pip install Plone plone.app.event[test] zope.testrunner -c https://dist.plone.org/release/6.0-dev/constraints.txt
|
||||
# Install the dev version of the package, mostly so we can safely point to the path with the tests.
|
||||
pip install -e "git+https://github.com/plone/plone.app.event.git#egg=plone.app.event"
|
||||
# icalendar is pinned in the constraints, but we want the current dev version.
|
||||
pip install -e {toxinidir}
|
||||
commands =
|
||||
zope-testrunner --test-path={envdir}/src/plone-app-event {posargs:-vc}
|
||||
|
|
Ładowanie…
Reference in New Issue