From 5898c2509a2d0735f0eca6a296b1d8166eff420e Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Sun, 30 Jun 2013 13:19:16 -0400 Subject: [PATCH] entry: Make Entry a subclass of dict This gives us the familiar interface, and moves key/value processing to a one-time-per-entry loop in ._fill_dict(). --- pycalendar/entry.py | 51 +++++++++++++++++++++++++++++++-------------- pycalendar/feed.py | 4 ++-- 2 files changed, 37 insertions(+), 18 deletions(-) diff --git a/pycalendar/entry.py b/pycalendar/entry.py index 13bcea6..90e0bac 100644 --- a/pycalendar/entry.py +++ b/pycalendar/entry.py @@ -8,7 +8,7 @@ from . import text as _text _LOG = _logging.getLogger(__name__) -class Entry (object): +class Entry (dict): r"""An iCalendar entry (e.g. VEVENT) Get an entry. @@ -44,10 +44,19 @@ class Entry (object): >>> entry.content # doctest: +ELLIPSIS 'BEGIN:VEVENT\r\nUID:...\r\nEND:VEVENT\r\n' - Use the ``.get*()`` methods to access individual fields. + ``Entry`` subclasses Python's ``dict``, so you can access raw + field values in the usual ways. + >>> entry['LOCATION'] + 'Snow Hill\\, Dover\\, Massachusetts' >>> entry.get('LOCATION') 'Snow Hill\\, Dover\\, Massachusetts' + >>> entry.get('missing') + >>> entry.get('missing', 'some default') + 'some default' + + You can also use ``get_text`` to unescape text fields. + >>> entry.get_text('LOCATION') 'Snow Hill, Dover, Massachusetts' """ @@ -59,6 +68,9 @@ class Entry (object): if content: self.process() + def __hash__(self): + return id(self) + def __str__(self): if self.content: return self.content.replace('\r\n', '\n').strip() @@ -68,7 +80,28 @@ class Entry (object): return '<{} type:{}>'.format(type(self).__name__, self.type) def process(self): + self.clear() self.unfold() + self._fill_dict() + + def _fill_dict(self): + for index,verb,expected in [ + [0, 'begin', 'BEGIN:{}'.format(self.type)], + [-1, 'end', 'END:{}'.format(self.type)], + ]: + if self.lines[index] != expected: + raise ValueError('entry should {} with {!r}, not {!r}'.format( + verb, expected, self.lines[index])) + for line in self.lines[1:-1]: + key,value = [x.strip() for x in line.split(':', 1)] + if key in ['BEGIN' or 'END']: + raise NotImplementedError(line) + if key in self: + if type(self[key]) == str: + self[key] = [self[key]] + self[key].append(value) + else: + self[key] = value def unfold(self): """Unfold wrapped lines @@ -92,20 +125,6 @@ class Entry (object): if semantic_line_chunks: self.lines.append(''.join(semantic_line_chunks)) - def get(self, key, **kwargs): - for k in kwargs.keys(): - if k != 'default': - raise TypeError( - 'get() got an unexpected keyword argument {!r}'.format( - k)) - for line in self.lines: - k,value = [x.strip() for x in line.split(':', 1)] - if k == key: - return value - if 'default' in kwargs: - return kwargs['default'] - raise KeyError(key) - def get_text(self, *args, **kwargs): value = self.get(*args, **kwargs) return _text.unescape(value) diff --git a/pycalendar/feed.py b/pycalendar/feed.py index 150a174..f104029 100644 --- a/pycalendar/feed.py +++ b/pycalendar/feed.py @@ -127,11 +127,11 @@ class Feed (set): _LOG.info('{!r}: begin {}'.format(self, _type)) stack.append(_type) if len(stack) == 2: - if entry: + if entry is not None: raise ValueError('double entry by line {}'.format(i)) entry = _entry.Entry(type=_type, content=[]) _LOG.info(stack) - if entry: + if entry is not None: entry.content.append(line) if line.startswith('END:'): _type = line.split(':', 1)[1] -- 2.26.2