Skip to content

Commit

Permalink
Merge e94c9b9 into 197e1b3
Browse files Browse the repository at this point in the history
  • Loading branch information
C4ptainCrunch committed Jul 28, 2019
2 parents 197e1b3 + e94c9b9 commit 270c204
Show file tree
Hide file tree
Showing 14 changed files with 250 additions and 235 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ doc/_build/*
htmlcov
.pytest_cache
/.pytest_cache/
.mypy_cache/

# pffff
.idea
Expand Down
52 changes: 30 additions & 22 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@
Ics.py changelog
============


**************
0.6
**************

- Add mypy


**************
0.5
**************
Expand Down Expand Up @@ -52,55 +60,55 @@ Last version to support Python 2.7 and 3.3.
This version is by far the one with the most contributors, thank you !

Highlights:
- Todo/VTODO support (thanks @tgamauf)
- Add event arithmetics (thanks @guyzmo)
- Support for alarms/`VALARM` (thanks @rkeilty)
- Support for categories (thanks @perette)
- Todo/VTODO support (thanks @tgamauf)
- Add event arithmetics (thanks @guyzmo)
- Support for alarms/`VALARM` (thanks @rkeilty)
- Support for categories (thanks @perette)

Misc:
- Make the parser work with tabbed whitespace (thanks @mrmadcow)
- Better error messages (thanks @guyzmo)
- Support input with missing `VERSION` (thanks @prashnts)
- Support for Time Transparency/`TRANSP` (thanks @GMLudo)
- All day events not omit the timezone (thanks @Trii)
- Multi-day events fixes (thanks @ConnyOnny)
- Fix `TZID` drop when `VTIMEZONE` is empty (thanks @ConnyOnny)
- Better test coverage (thanks @aureooms)
- Make the parser work with tabbed whitespace (thanks @mrmadcow)
- Better error messages (thanks @guyzmo)
- Support input with missing `VERSION` (thanks @prashnts)
- Support for Time Transparency/`TRANSP` (thanks @GMLudo)
- All day events not omit the timezone (thanks @Trii)
- Multi-day events fixes (thanks @ConnyOnny)
- Fix `TZID` drop when `VTIMEZONE` is empty (thanks @ConnyOnny)
- Better test coverage (thanks @aureooms)

Breaking Changes:
- Removed EventList class
- Removed EventList class

Thank you also to @davidjb, @etnarek, @jammon

*******
0.3.1
*******
- Pin arrow to 0.4.2
- Pin arrow to 0.4.2

*****
0.3
*****
- Events in an `EventList()` are now always sorted
- Freeze the version of Arrow (they made backwards-incompatible changes)
- Add a lot of tests
- Lots of small bugfixes
- Events in an `EventList()` are now always sorted
- Freeze the version of Arrow (they made backwards-incompatible changes)
- Add a lot of tests
- Lots of small bugfixes

*******
0.1.3
*******
- FIX : broken install. Again.
- FIX : broken install. Again.

*******
0.1.2
*******
- FIX : broken install
- FIX : broken install

*******
0.1.1
*******
- FIX : wrong `super()` and add output documentation
- FIX : wrong `super()` and add output documentation

****
0.1
****
- First version
- First version
1 change: 1 addition & 0 deletions dev/requirements-doc.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
sphinx
sphinxcontrib-napoleon
sphinx-autodoc-typehints
-r ../requirements.txt
1 change: 1 addition & 0 deletions dev/requirements-test.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ pytest-cov
pytest-flakes
pytest-pep8
pytest-sugar
mypy
2 changes: 2 additions & 0 deletions doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
'sphinx.ext.mathjax',
'sphinx.ext.viewcode',
'sphinxcontrib.napoleon',
'sphinx.ext.autodoc',
'sphinx_autodoc_typehints',
]

autodoc_member_order = 'groupwise'
Expand Down
112 changes: 57 additions & 55 deletions ics/alarm.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@
# -*- coding: utf-8 -*-

from __future__ import unicode_literals, absolute_import
from typing import Iterable, Union, Set, Dict, List, Callable, Optional, Type

import copy
from datetime import timedelta
from datetime import timedelta, datetime

from .component import Component
from .component import Component, Extractor
from .utils import (
arrow_to_iso,
escape_string,
Expand All @@ -20,46 +21,19 @@
from .parse import ContentLine, Container


class AlarmFactory(object):
"""
Factory class to get specific VALARM types, useful with `ics.component.Component._from_container` method.
"""

@classmethod
def get_type_from_action(cls, action_type):
# TODO: Implement EMAIL action
if action_type == 'DISPLAY':
return DisplayAlarm
elif action_type == 'AUDIO':
return AudioAlarm
elif action_type == 'NONE':
return None

raise ValueError('Invalid alarm action')

@classmethod
def get_type_from_container(cls, container):
action_type_lines = get_lines(container, 'ACTION')
if len(action_type_lines) > 1:
raise ValueError('Too many ACTION parameters in VALARM')

action_type = action_type_lines[0]
return AlarmFactory.get_type_from_action(action_type.value)


class Alarm(Component):
"""
A calendar event VALARM base class
"""

_TYPE = 'VALARM'
_EXTRACTORS = []
_OUTPUTS = []
_EXTRACTORS: List[Extractor] = []
_OUTPUTS: List[Callable] = []

def __init__(self,
trigger=None,
repeat=None,
duration=None):
trigger: Union[timedelta, datetime] = None,
repeat: int = None,
duration: timedelta = None):
"""
Instantiates a new :class:`ics.alarm.Alarm`.
Expand All @@ -74,9 +48,9 @@ def __init__(self,
ValueError: If trigger, repeat, or duration do not match the RFC spec.
"""
# Set initial values
self._trigger = None
self._repeat = None
self._duration = None
self._trigger: Optional[Union[timedelta, datetime]] = None
self._repeat: Optional[int] = None
self._duration: Optional[timedelta] = None

# Validate and parse
self.trigger = trigger
Expand All @@ -94,7 +68,7 @@ def __init__(self,
self._unused = Container(name='VALARM')

@property
def trigger(self):
def trigger(self) -> Optional[Union[timedelta, datetime]]:
"""The trigger condition for the alarm
| Returns either a timedelta or datetime object
Expand All @@ -104,16 +78,16 @@ def trigger(self):
return self._trigger

@trigger.setter
def trigger(self, value):
if type(value) is timedelta and value.total_seconds() < 0:
def trigger(self, value: Optional[Union[timedelta, datetime]]):
if isinstance(value, timedelta) and value.total_seconds() < 0:
raise ValueError('Trigger timespan must be positive')
elif type(value) is not timedelta:
elif isinstance(value, datetime):
value = get_arrow(value)

self._trigger = value

@property
def repeat(self):
def repeat(self) -> Optional[int]:
"""Number of times to repeat alarm
| Returns an integer for number of alarm repeats
Expand All @@ -122,14 +96,14 @@ def repeat(self):
return self._repeat

@repeat.setter
def repeat(self, value):
if value < 0:
def repeat(self, value: Optional[int]):
if value is not None and value < 0:
raise ValueError('Repeat must be great than or equal to 0.')

self._repeat = value

@property
def duration(self):
def duration(self) -> Optional[timedelta]:
"""Duration between alarm repeats
| Returns a timedelta object
Expand All @@ -138,8 +112,8 @@ def duration(self):
return self._duration

@duration.setter
def duration(self, value):
if value.total_seconds() < 0:
def duration(self, value: Optional[timedelta]):
if value is not None and value.total_seconds() < 0:
raise ValueError('Alarm duration timespan must be positive.')

self._duration = value
Expand All @@ -150,7 +124,7 @@ def action(self):
"""
raise NotImplementedError('Base class cannot be instantiated directly')

def __repr__(self):
def __repr__(self) -> str:
value = '{0} trigger:{1}'.format(type(self), self.trigger)
if self.repeat:
value += ' repeat:{0} duration:{1}'.format(self.repeat, self.duration)
Expand All @@ -160,10 +134,10 @@ def __repr__(self):
def __hash__(self):
return hash(repr(self))

def __ne__(self, other):
def __ne__(self, other) -> bool:
return not self.__eq__(other)

def __eq__(self, other):
def __eq__(self, other) -> bool:
"""Two alarms are considered equal if they have the same type and base values."""

return (type(self) is type(other) and
Expand All @@ -180,11 +154,39 @@ def clone(self):
return clone


class AlarmFactory(object):
"""
Factory class to get specific VALARM types, useful with `ics.component.Component._from_container` method.
"""

@classmethod
def get_type_from_action(cls, action_type: str) -> Type[Alarm]:
# TODO: Implement EMAIL action
if action_type == 'DISPLAY':
return DisplayAlarm
elif action_type == 'AUDIO':
return AudioAlarm
# FIXME mypy
# elif action_type == 'NONE':
# return None

raise ValueError('Invalid alarm action')

@classmethod
def get_type_from_container(cls, container: Container) -> Type[Alarm]:
action_type_lines = get_lines(container, 'ACTION')
if len(action_type_lines) > 1:
raise ValueError('Too many ACTION parameters in VALARM')

action_type = action_type_lines[0]
return AlarmFactory.get_type_from_action(action_type.value)


# ------------------
# ----- Inputs -----
# ------------------
@Alarm._extracts('TRIGGER', required=True)
def trigger(alarm, line):
def trigger(alarm: Alarm, line: ContentLine):
if not line.params or 'DURATION' in line.params.get('VALUE', ''):
alarm.trigger = parse_duration(line.value[1:])
else:
Expand All @@ -198,13 +200,13 @@ def trigger(alarm, line):


@Alarm._extracts('DURATION')
def duration(alarm, line):
def duration(alarm: Alarm, line: ContentLine):
if line:
alarm._duration = parse_duration(line.value)


@Alarm._extracts('REPEAT')
def repeat(alarm, line):
def repeat(alarm: Alarm, line: ContentLine):
if line:
alarm._repeat = int(line.value)

Expand Down Expand Up @@ -286,7 +288,7 @@ def __repr__(self):
# ----- Inputs -----
# ------------------
@DisplayAlarm._extracts('DESCRIPTION', required=True)
def description(alarm, line):
def description(alarm: DisplayAlarm, line: ContentLine):
alarm.description = unescape_string(line.value) if line else None


Expand Down Expand Up @@ -346,7 +348,7 @@ def __repr__(self):
# ----- Inputs -----
# ------------------
@AudioAlarm._extracts('ATTACH')
def attach(alarm, line):
def attach(alarm: AudioAlarm, line: ContentLine):
if line:
if line.value:
alarm.attach = unescape_string(line.value)
Expand Down
2 changes: 1 addition & 1 deletion ics/attendee.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

class Attendee(object):

def __init__(self, email, common_name=None, rsvp=None):
def __init__(self, email: str, common_name: str = None, rsvp: bool = None):
self.email = email
self.common_name = common_name or email
self.rsvp = rsvp
Expand Down

0 comments on commit 270c204

Please sign in to comment.