[CalendarServer-changes] [11615] PyCalendar/branches/json-2/src/pycalendar
source_changes at macosforge.org
source_changes at macosforge.org
Mon Aug 19 10:21:45 PDT 2013
Revision: 11615
http://trac.calendarserver.org//changeset/11615
Author: cdaboo at apple.com
Date: 2013-08-19 10:21:45 -0700 (Mon, 19 Aug 2013)
Log Message:
-----------
More re-factoring to clean-up some api.
Modified Paths:
--------------
PyCalendar/branches/json-2/src/pycalendar/icalendar/calendar.py
PyCalendar/branches/json-2/src/pycalendar/icalendar/tests/test_property.py
PyCalendar/branches/json-2/src/pycalendar/icalendar/tests/test_requeststatus.py
PyCalendar/branches/json-2/src/pycalendar/property.py
PyCalendar/branches/json-2/src/pycalendar/tests/test_urivalue.py
PyCalendar/branches/json-2/src/pycalendar/tests/test_validation.py
PyCalendar/branches/json-2/src/pycalendar/vcard/card.py
PyCalendar/branches/json-2/src/pycalendar/vcard/property.py
PyCalendar/branches/json-2/src/pycalendar/vcard/tests/test_adrvalue.py
PyCalendar/branches/json-2/src/pycalendar/vcard/tests/test_nvalue.py
PyCalendar/branches/json-2/src/pycalendar/vcard/tests/test_orgvalue.py
PyCalendar/branches/json-2/src/pycalendar/vcard/tests/test_property.py
Added Paths:
-----------
PyCalendar/branches/json-2/src/pycalendar/containerbase.py
Added: PyCalendar/branches/json-2/src/pycalendar/containerbase.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/containerbase.py (rev 0)
+++ PyCalendar/branches/json-2/src/pycalendar/containerbase.py 2013-08-19 17:21:45 UTC (rev 11615)
@@ -0,0 +1,215 @@
+##
+# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from cStringIO import StringIO
+from pycalendar.componentbase import ComponentBase
+from pycalendar.exceptions import InvalidData, ValidationError
+from pycalendar.parser import ParserContext
+from pycalendar.utils import readFoldedLine
+import json
+
+class ContainerBase(ComponentBase):
+ """
+ Represents the top-level component (i.e., VCALENDAR or vCARD)
+ """
+
+ sProdID = "-//mulberrymail.com//Mulberry v4.0//EN"
+ sDomain = "mulberrymail.com"
+
+ # These must be set by derived classes
+ sContainerDescriptor = None
+ sComponentType = None
+ sPropertyType = None
+
+ @classmethod
+ def setPRODID(cls, prodid):
+ cls.sProdID = prodid
+
+
+ @classmethod
+ def setDomain(cls, domain):
+ cls.sDomain = domain
+
+
+ def __init__(self, add_defaults=True):
+ super(ContainerBase, self).__init__()
+
+ if add_defaults:
+ self.addDefaultProperties()
+
+
+ def duplicate(self):
+ return super(ContainerBase, self).duplicate()
+
+
+ def getType(self):
+ raise NotImplementedError
+
+
+ def finalise(self):
+ pass
+
+
+ def validate(self, doFix=False, doRaise=False):
+ """
+ Validate the data in this component and optionally fix any problems. Return
+ a tuple containing two lists: the first describes problems that were fixed, the
+ second problems that were not fixed. Caller can then decide what to do with unfixed
+ issues.
+ """
+
+ # Optional raise behavior
+ fixed, unfixed = super(ContainerBase, self).validate(doFix)
+ if doRaise and unfixed:
+ raise ValidationError(";".join(unfixed))
+ return fixed, unfixed
+
+
+ @classmethod
+ def parseText(cls, data):
+
+ cal = cls(add_defaults=False)
+ if cal.parse(StringIO(data)):
+ return cal
+ else:
+ return None
+
+
+ def parse(self, ins):
+
+ result = False
+
+ self.setProperties({})
+
+ LOOK_FOR_CONTAINER = 0
+ GET_PROPERTY_OR_COMPONENT = 1
+
+ state = LOOK_FOR_CONTAINER
+
+ # Get lines looking for start of calendar
+ lines = [None, None]
+ comp = self
+ compend = None
+ componentstack = []
+
+ while readFoldedLine(ins, lines):
+
+ line = lines[0]
+
+ if state == LOOK_FOR_CONTAINER:
+
+ # Look for start
+ if line == self.getBeginDelimiter():
+ # Next state
+ state = GET_PROPERTY_OR_COMPONENT
+
+ # Indicate success at this point
+ result = True
+
+ # Handle blank line
+ elif len(line) == 0:
+ # Raise if requested, otherwise just ignore
+ if ParserContext.BLANK_LINES_IN_DATA == ParserContext.PARSER_RAISE:
+ raise InvalidData("%s data has blank lines" % (self.sContainerDescriptor,))
+
+ # Unrecognized data
+ else:
+ raise InvalidData("%s data not recognized" % (self.sContainerDescriptor,), line)
+
+ elif state == GET_PROPERTY_OR_COMPONENT:
+
+ # Parse property or look for start of component
+ if line.startswith("BEGIN:") and self.sComponentType is not None:
+
+ # Push previous details to stack
+ componentstack.append((comp, compend,))
+
+ # Start a new component
+ comp = self.sComponentType.makeComponent(line[6:], comp)
+ compend = comp.getEndDelimiter()
+
+ # Look for end of object
+ elif line == self.getEndDelimiter():
+
+ # Finalise the current calendar
+ self.finalise()
+
+ # Change state
+ state = LOOK_FOR_CONTAINER
+
+ # Look for end of current component
+ elif line == compend:
+
+ # Finalise the component (this caches data from the properties)
+ comp.finalise()
+
+ # Embed component in parent and reset to use parent
+ componentstack[-1][0].addComponent(comp)
+ comp, compend = componentstack.pop()
+
+ # Blank line
+ elif len(line) == 0:
+ # Raise if requested, otherwise just ignore
+ if ParserContext.BLANK_LINES_IN_DATA == ParserContext.PARSER_RAISE:
+ raise InvalidData("%s data has blank lines" % (self.sContainerDescriptor,))
+
+ # Must be a property
+ else:
+
+ # Parse parameter/value for top-level calendar item
+ prop = self.sPropertyType.parseText(line)
+
+ # Check for valid property
+ if comp is self:
+ if not comp.validProperty(prop):
+ raise InvalidData("Invalid property", str(prop))
+ else:
+ comp.addProperty(prop)
+ else:
+ comp.addProperty(prop)
+
+ # Check for truncated data
+ if state != LOOK_FOR_CONTAINER:
+ raise InvalidData("%s data not complete" % (self.sContainerDescriptor,))
+
+ # Validate some things
+ if result and not self.hasProperty("VERSION"):
+ raise InvalidData("%s missing VERSION" % (self.sContainerDescriptor,))
+
+ return result
+
+
+ @classmethod
+ def parseJSONText(cls, data):
+
+ try:
+ return cls.parseJSON(json.loads(data), None, cls(add_defaults=False))
+ except Exception:
+ return None
+
+
+ def getTextJSON(self):
+ jobject = []
+ self.writeJSON(jobject)
+ return json.dumps(jobject[0], indent=2, separators=(',', ':'))
+
+
+ def addDefaultProperties(self):
+ raise NotImplementedError
+
+
+ def validProperty(self, prop):
+ raise NotImplementedError
Modified: PyCalendar/branches/json-2/src/pycalendar/icalendar/calendar.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/icalendar/calendar.py 2013-08-19 01:31:02 UTC (rev 11614)
+++ PyCalendar/branches/json-2/src/pycalendar/icalendar/calendar.py 2013-08-19 17:21:45 UTC (rev 11615)
@@ -16,9 +16,9 @@
from cStringIO import StringIO
from pycalendar import xmlutils
-from pycalendar.componentbase import ComponentBase
+from pycalendar.containerbase import ContainerBase
from pycalendar.datetime import DateTime
-from pycalendar.exceptions import InvalidData, ValidationError
+from pycalendar.exceptions import InvalidData
from pycalendar.icalendar import definitions, xmldefinitions
from pycalendar.icalendar.component import Component
from pycalendar.icalendar.componentexpanded import ComponentExpanded
@@ -33,7 +33,7 @@
import json
import xml.etree.cElementTree as XML
-class Calendar(ComponentBase):
+class Calendar(ContainerBase):
REMOVE_ALL = 0
REMOVE_ONLY_THIS = 1
@@ -45,18 +45,10 @@
sProdID = "-//mulberrymail.com//Mulberry v4.0//EN"
sDomain = "mulberrymail.com"
+ sContainerDescriptor = "iCalendar"
sComponentType = Component
sPropertyType = Property
- @staticmethod
- def setPRODID(prodid):
- Calendar.sProdID = prodid
-
-
- @staticmethod
- def setDomain(domain):
- Calendar.sDomain = domain
-
propertyCardinality_1 = (
definitions.cICalProperty_PRODID,
definitions.cICalProperty_VERSION,
@@ -77,10 +69,7 @@
self.mMasterComponentsByTypeAndUID = collections.defaultdict(lambda: collections.defaultdict(list))
self.mOverriddenComponentsByUID = collections.defaultdict(list)
- if add_defaults:
- self.addDefaultProperties()
-
def duplicate(self):
other = super(Calendar, self).duplicate()
other.mName = self.mName
@@ -182,21 +171,6 @@
self.mDescription = temps
- def validate(self, doFix=False, doRaise=False):
- """
- Validate the data in this component and optionally fix any problems. Return
- a tuple containing two lists: the first describes problems that were fixed, the
- second problems that were not fixed. Caller can then decide what to do with unfixed
- issues.
- """
-
- # Optional raise behavior
- fixed, unfixed = super(Calendar, self).validate(doFix)
- if doRaise and unfixed:
- raise ValidationError(";".join(unfixed))
- return fixed, unfixed
-
-
def sortedComponentNames(self):
return (
definitions.cICalComponent_VTIMEZONE,
@@ -217,122 +191,14 @@
)
- @staticmethod
- def parseText(data):
-
- cal = Calendar(add_defaults=False)
- if cal.parse(StringIO(data)):
- return cal
- else:
- return None
-
-
def parse(self, ins):
- result = False
+ result = super(Calendar, self).parse(ins)
- self.setProperties({})
-
- LOOK_FOR_VCALENDAR = 0
- GET_PROPERTY_OR_COMPONENT = 1
-
- state = LOOK_FOR_VCALENDAR
-
- # Get lines looking for start of calendar
- lines = [None, None]
- comp = self
- compend = None
- componentstack = []
-
- while readFoldedLine(ins, lines):
-
- line = lines[0]
-
- if state == LOOK_FOR_VCALENDAR:
-
- # Look for start
- if line == self.getBeginDelimiter():
- # Next state
- state = GET_PROPERTY_OR_COMPONENT
-
- # Indicate success at this point
- result = True
-
- # Handle blank line
- elif len(line) == 0:
- # Raise if requested, otherwise just ignore
- if ParserContext.BLANK_LINES_IN_DATA == ParserContext.PARSER_RAISE:
- raise InvalidData("iCalendar data has blank lines")
-
- # Unrecognized data
- else:
- raise InvalidData("iCalendar data not recognized", line)
-
- elif state == GET_PROPERTY_OR_COMPONENT:
-
- # Parse property or look for start of component
- if line.startswith("BEGIN:"):
-
- # Push previous details to stack
- componentstack.append((comp, compend,))
-
- # Start a new component
- comp = Component.makeComponent(line[6:], comp)
- compend = comp.getEndDelimiter()
-
- # Look for end of object
- elif line == self.getEndDelimiter():
-
- # Finalise the current calendar
- self.finalise()
-
- # Change state
- state = LOOK_FOR_VCALENDAR
-
- # Look for end of current component
- elif line == compend:
-
- # Finalise the component (this caches data from the properties)
- comp.finalise()
-
- # Embed component in parent and reset to use parent
- componentstack[-1][0].addComponent(comp)
- comp, compend = componentstack.pop()
-
- # Blank line
- elif len(line) == 0:
- # Raise if requested, otherwise just ignore
- if ParserContext.BLANK_LINES_IN_DATA == ParserContext.PARSER_RAISE:
- raise InvalidData("iCalendar data has blank lines")
-
- # Must be a property
- else:
-
- # Parse parameter/value for top-level calendar item
- prop = Property()
- if prop.parse(line):
-
- # Check for valid property
- if comp is self:
- if not comp.validProperty(prop):
- raise InvalidData("Invalid property", str(prop))
- elif not comp.ignoreProperty(prop):
- comp.addProperty(prop)
- else:
- comp.addProperty(prop)
-
- # Check for truncated data
- if state != LOOK_FOR_VCALENDAR:
- raise InvalidData("iCalendar data not complete")
-
# We need to store all timezones in the static object so they can be accessed by any date object
from pycalendar.timezonedb import TimezoneDatabase
TimezoneDatabase.mergeTimezones(self, self.getComponents(definitions.cICalComponent_VTIMEZONE))
- # Validate some things
- if result and not self.hasProperty("VERSION"):
- raise InvalidData("iCalendar missing VERSION")
-
return result
@@ -421,12 +287,11 @@
else:
# Parse parameter/value for top-level calendar item
- prop = Property()
- if prop.parse(lines[0]):
+ prop = Property.parseText(lines[0])
- # Check for valid property
- if comp is not self:
- comp.addProperty(prop)
+ # Check for valid property
+ if comp is not self:
+ comp.addProperty(prop)
# Exit if we have one - ignore all the rest
if state == GOT_VCALENDAR:
@@ -500,26 +365,19 @@
return root
- @staticmethod
- def parseJSONText(data):
-
- return Calendar.parseJSON(json.loads(data), None, Calendar(add_defaults=False))
-
-
def getTextJSON(self, includeTimezones=False):
- jobject = self.writeJSON(includeTimezones)
- return json.dumps(jobject, indent=2, separators=(',', ':'))
+ jobject = []
+ self.writeJSON(jobject, includeTimezones)
+ return json.dumps(jobject[0], indent=2, separators=(',', ':'))
- def writeJSON(self, includeTimezones=False):
+ def writeJSON(self, jobject, includeTimezones=False):
# Make sure all required timezones are in this object
if includeTimezones:
self.includeTimezones()
# Root node structure
- vcalendar = []
- super(Calendar, self).writeJSON(vcalendar)
- return vcalendar[0]
+ super(Calendar, self).writeJSON(jobject)
# Get expanded components
@@ -724,10 +582,6 @@
return True
- def ignoreProperty(self, prop):
- return False #prop.getName() in (definitions.cICalProperty_VERSION, definitions.cICalProperty_CALSCALE, definitions.cICalProperty_PRODID)
-
-
def includeTimezones(self):
# Get timezone names from each component
tzids = set()
Modified: PyCalendar/branches/json-2/src/pycalendar/icalendar/tests/test_property.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/icalendar/tests/test_property.py 2013-08-19 01:31:02 UTC (rev 11614)
+++ PyCalendar/branches/json-2/src/pycalendar/icalendar/tests/test_property.py 2013-08-19 17:21:45 UTC (rev 11615)
@@ -62,8 +62,7 @@
def testParseGenerate(self):
for data in TestProperty.test_data:
- prop = Property()
- prop.parse(data)
+ prop = Property.parseText(data)
propstr = str(prop).replace("\r\n ", "")
self.assertEqual(propstr[:-2], data, "Failed parse/generate: %s to %s" % (data, propstr,))
@@ -71,10 +70,8 @@
def testEquality(self):
for data in TestProperty.test_data:
- prop1 = Property()
- prop1.parse(data)
- prop2 = Property()
- prop2.parse(data)
+ prop1 = Property.parseText(data)
+ prop2 = Property.parseText(data)
self.assertEqual(prop1, prop2, "Failed equality: %s" % (data,))
@@ -98,8 +95,7 @@
save = ParserContext.INVALID_ESCAPE_SEQUENCES
for data in test_bad_data:
ParserContext.INVALID_ESCAPE_SEQUENCES = ParserContext.PARSER_RAISE
- prop = Property()
- self.assertRaises(InvalidProperty, prop.parse, data)
+ self.assertRaises(InvalidProperty, Property.parseText, data)
ParserContext.INVALID_ESCAPE_SEQUENCES = save
@@ -107,8 +103,7 @@
hashes = []
for item in TestProperty.test_data:
- prop = Property()
- prop.parse(item)
+ prop = Property.parseText(item)
hashes.append(hash(prop))
hashes.sort()
for i in range(1, len(hashes)):
@@ -133,24 +128,21 @@
def testGEOValueRoundtrip(self):
data = "GEO:123.456;789.101"
- prop = Property()
- prop.parse(data)
+ prop = Property.parseText(data)
self.assertEqual(str(prop), data + "\r\n")
def testUnknownValueRoundtrip(self):
data = "X-FOO:Text, not escaped"
- prop = Property()
- prop.parse(data)
+ prop = Property.parseText(data)
self.assertEqual(str(prop), data + "\r\n")
prop = Property("X-FOO", "Text, not escaped")
self.assertEqual(str(prop), data + "\r\n")
data = "X-FOO:Text\\, escaped\\n"
- prop = Property()
- prop.parse(data)
+ prop = Property.parseText(data)
self.assertEqual(str(prop), data + "\r\n")
prop = Property("X-FOO", "Text\\, escaped\\n")
@@ -162,8 +154,7 @@
Property.registerDefaultValue("X-SPECIAL-REGISTRATION", Value.VALUETYPE_TEXT)
data = "X-SPECIAL-REGISTRATION:Text\\, escaped\\n"
- prop = Property()
- prop.parse(data)
+ prop = Property.parseText(data)
self.assertEqual(str(prop), "X-SPECIAL-REGISTRATION:Text\\, escaped\\n\r\n")
prop = Property("X-SPECIAL-REGISTRATION", "Text, escaped\n")
@@ -180,12 +171,10 @@
self.assertEqual(str(prop), "X-FOO;X-BAR=^'Check^';X-BAR2=Check^nThis\tOut^n:Test\r\n")
data = "X-FOO;X-BAR=^'Check^':Test"
- prop = Property()
- prop.parse(data)
+ prop = Property.parseText(data)
self.assertEqual(prop.getParameterValue("X-BAR"), "\"Check\"")
data = "X-FOO;X-BAR=^'Check^';X-BAR2=Check^nThis\tOut^n:Test"
- prop = Property()
- prop.parse(data)
+ prop = Property.parseText(data)
self.assertEqual(prop.getParameterValue("X-BAR"), "\"Check\"")
self.assertEqual(prop.getParameterValue("X-BAR2"), "Check\nThis\tOut\n")
Modified: PyCalendar/branches/json-2/src/pycalendar/icalendar/tests/test_requeststatus.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/icalendar/tests/test_requeststatus.py 2013-08-19 01:31:02 UTC (rev 11614)
+++ PyCalendar/branches/json-2/src/pycalendar/icalendar/tests/test_requeststatus.py 2013-08-19 17:21:45 UTC (rev 11615)
@@ -90,6 +90,5 @@
)
for item in items:
- req = Property()
- req.parse(item)
+ req = Property.parseText(item)
self.assertEqual(req.getText(), item + "\r\n", "Failed to parse and re-generate '%s'" % (item,))
Modified: PyCalendar/branches/json-2/src/pycalendar/property.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/property.py 2013-08-19 01:31:02 UTC (rev 11614)
+++ PyCalendar/branches/json-2/src/pycalendar/property.py 2013-08-19 17:21:45 UTC (rev 11615)
@@ -207,38 +207,65 @@
return None
- def parse(self, data):
- # Look for parameter or value delimiter
- prop_name, txt = stringutils.strduptokenstr(data, ";:")
- if not prop_name:
- raise InvalidProperty("Invalid property", data)
+ @classmethod
+ def parseText(cls, data):
+ """
+ Parse the text format data and return a L{Property}
- # We have the name
- if self.sUsesGroup:
- # Check for group prefix
- splits = prop_name.split(".", 1)
- if len(splits) == 2:
- # We have both group and name
- self.mGroup = splits[0]
- self.mName = splits[1]
+ @param data: text data
+ @type data: C{str}
+ """
+
+ try:
+ prop = cls()
+
+ # Look for parameter or value delimiter
+ prop_name, txt = stringutils.strduptokenstr(data, ";:")
+ if not prop_name:
+ raise InvalidProperty("Invalid property", data)
+
+ # Get the name
+ if prop.sUsesGroup:
+ # Check for group prefix
+ splits = prop_name.split(".", 1)
+ if len(splits) == 2:
+ # We have both group and name
+ prop.mGroup = splits[0]
+ prop.mName = splits[1]
+ else:
+ # We have the name
+ prop.mName = prop_name
else:
- # We have the name
- self.mName = prop_name
- else:
- self.mName = prop_name
+ prop.mName = prop_name
- # Now loop getting data
- txt = self.parseParameters(txt, data)
- self.createValue(txt)
+ # Get the parameters
+ txt = prop.parseTextParameters(txt, data)
- # We must have a value of some kind
- if self.mValue is None:
+ # Tidy first
+ prop.mValue = None
+
+ # Get value type from property name
+ value_type = prop.determineValueType()
+
+ # Check for multivalued
+ if prop.mName.upper() in prop.sMultiValues:
+ prop.mValue = MultiValue(value_type)
+ else:
+ # Create the type
+ prop.mValue = Value.createFromType(value_type)
+
+ # Now parse the data
+ prop.mValue.parse(txt, prop.sVariant)
+
+ prop._postCreateValue(value_type)
+
+ return prop
+
+ except Exception:
raise InvalidProperty("Invalid property", data)
- return True
-
- def parseParameters(self, txt, data):
+ def parseTextParameters(self, txt, data):
"""
Parse parameters, return string point at value.
"""
@@ -403,7 +430,11 @@
try:
prop = cls()
+
+ # Get the name
prop.mName = jobject[0].upper()
+
+ # Get the parameters
if jobject[1]:
for name, value in jobject[1].items():
# Now add parameter value
@@ -411,19 +442,15 @@
attrvalue = Parameter(name=name, value=value)
prop.mParameters.setdefault(name, []).append(attrvalue)
+ # Get default value type from property name and insert a VALUE parameter if current value type is not default
value_type = cls.sValueTypeMap.get(jobject[2].upper(), Value.VALUETYPE_UNKNOWN)
-
- # Get default value type from property name and insert a VALUE parameter if current value type is not default
default_type = cls.sDefaultValueTypeMap.get(prop.mName.upper(), Value.VALUETYPE_UNKNOWN)
if default_type != value_type:
- attrvalue = Parameter(name=cls.sValue, value=value_type)
+ attrvalue = Parameter(name=cls.sValue, value=jobject[2].upper())
prop.mParameters.setdefault(cls.sValue, []).append(attrvalue)
- # Check for specials
- if prop.mName.upper() in cls.sSpecialVariants:
- # Make sure we have the default value for the special
- if value_type == cls.sDefaultValueTypeMap.get(prop.mName.upper(), Value.VALUETYPE_UNKNOWN):
- value_type = cls.sSpecialVariants[prop.mName.upper()]
+ # Get value type from property name
+ value_type = prop.determineValueType()
# Check for multivalued
values = jobject[3:]
@@ -477,10 +504,7 @@
self.mValue.writeJSON(prop)
- def createValue(self, data):
- # Tidy first
- self.mValue = None
-
+ def determineValueType(self):
# Get value type from property name
value_type = self.sDefaultValueTypeMap.get(self.mName.upper(), Value.VALUETYPE_UNKNOWN)
@@ -496,6 +520,16 @@
if value_type == self.sDefaultValueTypeMap.get(self.mName.upper(), Value.VALUETYPE_UNKNOWN):
value_type = self.sSpecialVariants[self.mName.upper()]
+ return value_type
+
+
+ def createValue(self, data):
+ # Tidy first
+ self.mValue = None
+
+ # Get value type from property name
+ value_type = self.determineValueType()
+
# Check for multivalued
if self.mName.upper() in self.sMultiValues:
self.mValue = MultiValue(value_type)
Modified: PyCalendar/branches/json-2/src/pycalendar/tests/test_urivalue.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/tests/test_urivalue.py 2013-08-19 01:31:02 UTC (rev 11614)
+++ PyCalendar/branches/json-2/src/pycalendar/tests/test_urivalue.py 2013-08-19 17:21:45 UTC (rev 11615)
@@ -68,8 +68,7 @@
)
for item, result in items:
- prop = Property()
- prop.parse(item)
+ prop = Property.parseText(item)
test = prop.getText()
self.assertEqual(test, result + "\r\n", "Failed to parse and re-generate '%s'" % (item,))
@@ -81,7 +80,6 @@
)
for item, result in items:
- prop = Property()
- prop.parse(item)
+ prop = Property.parseText(item)
test = prop.getText()
self.assertEqual(test, result + "\r\n", "Failed to parse and re-generate '%s'" % (item,))
Modified: PyCalendar/branches/json-2/src/pycalendar/tests/test_validation.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/tests/test_validation.py 2013-08-19 01:31:02 UTC (rev 11614)
+++ PyCalendar/branches/json-2/src/pycalendar/tests/test_validation.py 2013-08-19 17:21:45 UTC (rev 11615)
@@ -40,8 +40,7 @@
)
for prop, test, result in props:
- property = Property()
- property.parse(prop)
+ property = Property.parseText(prop)
self.assertEqual(PropertyValueChecks.stringValue(test, property), result)
@@ -55,8 +54,7 @@
)
for prop, result in props:
- property = Property()
- property.parse(prop)
+ property = Property.parseText(prop)
self.assertEqual(PropertyValueChecks.alwaysUTC(property), result)
@@ -72,8 +70,7 @@
)
for prop, low, high, result in props:
- property = Property()
- property.parse(prop)
+ property = Property.parseText(prop)
self.assertEqual(PropertyValueChecks.numericRange(low, high, property), result)
@@ -87,6 +84,5 @@
)
for prop, result in props:
- property = Property()
- property.parse(prop)
+ property = Property.parseText(prop)
self.assertEqual(PropertyValueChecks.positiveIntegerOrZero(property), result)
Modified: PyCalendar/branches/json-2/src/pycalendar/vcard/card.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/vcard/card.py 2013-08-19 01:31:02 UTC (rev 11614)
+++ PyCalendar/branches/json-2/src/pycalendar/vcard/card.py 2013-08-19 17:21:45 UTC (rev 11615)
@@ -14,10 +14,8 @@
# limitations under the License.
##
-from cStringIO import StringIO
-from pycalendar.componentbase import ComponentBase
-from pycalendar.exceptions import InvalidData, \
- ValidationError
+from pycalendar.containerbase import ContainerBase
+from pycalendar.exceptions import InvalidData
from pycalendar.parser import ParserContext
from pycalendar.utils import readFoldedLine
from pycalendar.vcard import definitions
@@ -25,25 +23,13 @@
Property_PRODID, Property_UID
from pycalendar.vcard.property import Property
from pycalendar.vcard.validation import VCARD_VALUE_CHECKS
-import json
-class Card(ComponentBase):
+class Card(ContainerBase):
- sProdID = "-//mulberrymail.com//Mulberry v4.0//EN"
- sDomain = "mulberrymail.com"
-
+ sContainerDescriptor = "vCard"
sComponentType = None
sPropertyType = Property
- @staticmethod
- def setPRODID(prodid):
- Card.sProdID = prodid
-
-
- @staticmethod
- def setDomain(domain):
- Card.sDomain = domain
-
propertyCardinality_1 = (
definitions.Property_VERSION,
definitions.Property_N,
@@ -62,13 +48,6 @@
propertyValueChecks = VCARD_VALUE_CHECKS
- def __init__(self, add_defaults=True):
- super(Card, self).__init__()
-
- if add_defaults:
- self.addDefaultProperties()
-
-
def duplicate(self):
return super(Card, self).duplicate()
@@ -77,25 +56,6 @@
return VCARD
- def finalise(self):
- pass
-
-
- def validate(self, doFix=False, doRaise=False):
- """
- Validate the data in this component and optionally fix any problems. Return
- a tuple containing two lists: the first describes problems that were fixed, the
- second problems that were not fixed. Caller can then decide what to do with unfixed
- issues.
- """
-
- # Optional raise behavior
- fixed, unfixed = super(Card, self).validate(doFix)
- if doRaise and unfixed:
- raise ValidationError(";".join(unfixed))
- return fixed, unfixed
-
-
def sortedPropertyKeyOrder(self):
return (
Property_VERSION,
@@ -167,14 +127,13 @@
else:
# Parse parameter/value for top-level calendar item
- prop = Property()
- if prop.parse(line):
+ prop = Property.parseText(line)
- # Check for valid property
- if not card.validProperty(prop):
- raise InvalidData("Invalid property", str(prop))
- else:
- card.addProperty(prop)
+ # Check for valid property
+ if not card.validProperty(prop):
+ raise InvalidData("Invalid property", str(prop))
+ else:
+ card.addProperty(prop)
# Check for truncated data
if state != LOOK_FOR_VCARD:
@@ -183,111 +142,6 @@
return results
- @staticmethod
- def parseText(data):
-
- cal = Card(add_defaults=False)
- if cal.parse(StringIO(data)):
- return cal
- else:
- return None
-
-
- def parse(self, ins):
-
- result = False
-
- self.setProperties({})
-
- LOOK_FOR_VCARD = 0
- GET_PROPERTY = 1
-
- state = LOOK_FOR_VCARD
-
- # Get lines looking for start of calendar
- lines = [None, None]
-
- while readFoldedLine(ins, lines):
-
- line = lines[0]
- if state == LOOK_FOR_VCARD:
-
- # Look for start
- if line == self.getBeginDelimiter():
- # Next state
- state = GET_PROPERTY
-
- # Indicate success at this point
- result = True
-
- # Handle blank line
- elif len(line) == 0:
- # Raise if requested, otherwise just ignore
- if ParserContext.BLANK_LINES_IN_DATA == ParserContext.PARSER_RAISE:
- raise InvalidData("vCard data has blank lines")
-
- # Unrecognized data
- else:
- raise InvalidData("vCard data not recognized", line)
-
- elif state == GET_PROPERTY:
-
- # Look for end of object
- if line == self.getEndDelimiter():
-
- # Finalise the current calendar
- self.finalise()
-
- # Change state
- state = LOOK_FOR_VCARD
-
- # Blank line
- elif len(line) == 0:
- # Raise if requested, otherwise just ignore
- if ParserContext.BLANK_LINES_IN_DATA == ParserContext.PARSER_RAISE:
- raise InvalidData("vCard data has blank lines")
-
- # Must be a property
- else:
-
- # Parse parameter/value for top-level calendar item
- prop = Property()
- try:
- if prop.parse(line):
-
- # Check for valid property
- if not self.validProperty(prop):
- raise InvalidData("Invalid property", str(prop))
- else:
- self.addProperty(prop)
- except IndexError:
- print line
-
- # Check for truncated data
- if state != LOOK_FOR_VCARD:
- raise InvalidData("vCard data not complete", "")
-
- # Validate some things
- if result and not self.hasProperty("VERSION"):
- raise InvalidData("vCard missing VERSION", "")
-
- return result
-
-
- @staticmethod
- def parseJSONText(data):
-
- try:
- return Card.parseJSON(json.loads(data), None, Card(add_defaults=False))
- except Exception:
- return None
-
-
- def getTextJSON(self, includeTimezones=False):
- jobject = self.writeJSON(includeTimezones)
- return json.dumps(jobject, indent=2, separators=(',', ':'))
-
-
def addDefaultProperties(self):
self.addProperty(Property(definitions.Property_PRODID, Card.sProdID))
self.addProperty(Property(definitions.Property_VERSION, "3.0"))
Modified: PyCalendar/branches/json-2/src/pycalendar/vcard/property.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/vcard/property.py 2013-08-19 01:31:02 UTC (rev 11614)
+++ PyCalendar/branches/json-2/src/pycalendar/vcard/property.py 2013-08-19 17:21:45 UTC (rev 11615)
@@ -209,7 +209,7 @@
)
- def parseParameters(self, txt, data):
+ def parseTextParameters(self, txt, data):
"""
Parse parameters, return string point at value.
"""
Modified: PyCalendar/branches/json-2/src/pycalendar/vcard/tests/test_adrvalue.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/vcard/tests/test_adrvalue.py 2013-08-19 01:31:02 UTC (rev 11614)
+++ PyCalendar/branches/json-2/src/pycalendar/vcard/tests/test_adrvalue.py 2013-08-19 17:21:45 UTC (rev 11615)
@@ -56,7 +56,6 @@
)
for item, result in items:
- prop = Property()
- prop.parse(item)
+ prop = Property.parseText(item)
test = prop.getText()
self.assertEqual(test, result + "\r\n", "Failed to parse and re-generate '%s'" % (item,))
Modified: PyCalendar/branches/json-2/src/pycalendar/vcard/tests/test_nvalue.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/vcard/tests/test_nvalue.py 2013-08-19 01:31:02 UTC (rev 11614)
+++ PyCalendar/branches/json-2/src/pycalendar/vcard/tests/test_nvalue.py 2013-08-19 17:21:45 UTC (rev 11615)
@@ -53,7 +53,6 @@
)
for item, result in items:
- prop = Property()
- prop.parse(item)
+ prop = Property.parseText(item)
test = prop.getText()
self.assertEqual(test, result + "\r\n", "Failed to parse and re-generate '%s'" % (item,))
Modified: PyCalendar/branches/json-2/src/pycalendar/vcard/tests/test_orgvalue.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/vcard/tests/test_orgvalue.py 2013-08-19 01:31:02 UTC (rev 11614)
+++ PyCalendar/branches/json-2/src/pycalendar/vcard/tests/test_orgvalue.py 2013-08-19 17:21:45 UTC (rev 11615)
@@ -47,7 +47,6 @@
)
for item, result in items:
- prop = Property()
- prop.parse(item)
+ prop = Property.parseText(item)
test = prop.getText()
self.assertEqual(test, result + "\r\n", "Failed to parse and re-generate '%s'" % (item,))
Modified: PyCalendar/branches/json-2/src/pycalendar/vcard/tests/test_property.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/vcard/tests/test_property.py 2013-08-19 01:31:02 UTC (rev 11614)
+++ PyCalendar/branches/json-2/src/pycalendar/vcard/tests/test_property.py 2013-08-19 17:21:45 UTC (rev 11615)
@@ -41,8 +41,7 @@
def testParseGenerate(self):
for data in TestProperty.test_data:
- prop = Property()
- prop.parse(data)
+ prop = Property.parseText(data)
propstr = str(prop)
self.assertEqual(propstr[:-2], data, "Failed parse/generate: %s to %s" % (data, propstr,))
@@ -50,10 +49,8 @@
def testEquality(self):
for data in TestProperty.test_data:
- prop1 = Property()
- prop1.parse(data)
- prop2 = Property()
- prop2.parse(data)
+ prop1 = Property.parseText(data)
+ prop2 = Property.parseText(data)
self.assertEqual(prop1, prop2, "Failed equality: %s" % (data,))
@@ -66,8 +63,7 @@
save = ParserContext.INVALID_ESCAPE_SEQUENCES
for data in test_bad_data:
ParserContext.INVALID_ESCAPE_SEQUENCES = ParserContext.PARSER_RAISE
- prop = Property()
- self.assertRaises(InvalidProperty, prop.parse, data)
+ self.assertRaises(InvalidProperty, Property.parseText, data)
ParserContext.INVALID_ESCAPE_SEQUENCES = save
@@ -75,8 +71,7 @@
hashes = []
for item in TestProperty.test_data:
- prop = Property()
- prop.parse(item)
+ prop = Property.parseText(item)
hashes.append(hash(prop))
hashes.sort()
for i in range(1, len(hashes)):
@@ -107,12 +102,10 @@
self.assertEqual(str(prop), "X-FOO;X-BAR=^'Check^';X-BAR2=Check^nThis\tOut^n:Test\r\n")
data = "X-FOO;X-BAR=^'Check^':Test"
- prop = Property()
- prop.parse(data)
+ prop = Property.parseText(data)
self.assertEqual(prop.getParameterValue("X-BAR"), "\"Check\"")
data = "X-FOO;X-BAR=^'Check^';X-BAR2=Check^nThis\tOut^n:Test"
- prop = Property()
- prop.parse(data)
+ prop = Property.parseText(data)
self.assertEqual(prop.getParameterValue("X-BAR"), "\"Check\"")
self.assertEqual(prop.getParameterValue("X-BAR2"), "Check\nThis\tOut\n")
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20130819/6d5c230c/attachment-0001.html>
More information about the calendarserver-changes
mailing list