docs: def diff(left, right):
      """
      Take two VCALENDAR components, compare VEVENTs and VTODOs in them,
      return a list of object pairs containing just UID and the bits
      that didn't match, using None for objects that weren't present in one
      version or the other.
  
      When there are multiple ContentLines in one VEVENT, for instance many
      DESCRIPTION lines, such lines original order is assumed to be
      meaningful.  Order is also preserved when comparing (the unlikely case
      of) multiple parameters of the same type in a ContentLine
  
      """
  
      def processComponentLists(leftList, rightList):
          output = []
          rightIndex = 0
          rightListSize = len(rightList)
  
          for comp in leftList:
              if rightIndex >= rightListSize:
                  output.append((comp, None))
              else:
                  leftKey  = getSortKey(comp)
                  rightComp = rightList[rightIndex]
                  rightKey = getSortKey(rightComp)
                  while leftKey > rightKey:
                      output.append((None, rightComp))
                      rightIndex += 1
                      if rightIndex >= rightListSize:
                          output.append((comp, None))
                          break
                      else:
                          rightComp = rightList[rightIndex]
                          rightKey = getSortKey(rightComp)
  
                  if leftKey < rightKey:
                      output.append((comp, None))
                  elif leftKey == rightKey:
                      rightIndex += 1
                      matchResult = processComponentPair(comp, rightComp)
                      if matchResult is not None:
                          output.append(matchResult)
  
          return output
  
      def newComponent(name, body):
          if body is None:
              return None
          else:
              c = Component(name)
              c.behavior = getBehavior(name)
              c.isNative = True
              return c
  
      def processComponentPair(leftComp, rightComp):
          """
          Return None if a match, or a pair of components including UIDs and
          any differing children.
  
          """
          leftChildKeys = leftComp.contents.keys()
          rightChildKeys = rightComp.contents.keys()
  
          differentContentLines = []
          differentComponents = {}
  
          for key in leftChildKeys:
              rightList = rightComp.contents.get(key, [])
              if isinstance(leftComp.contents[key][0], Component):
                  compDifference = processComponentLists(leftComp.contents[key],
                                                         rightList)
                  if len(compDifference) > 0:
                      differentComponents[key] = compDifference
  
              elif leftComp.contents[key] != rightList:
                  differentContentLines.append((leftComp.contents[key],
                                                rightList))
  
          for key in rightChildKeys:
              if key not in leftChildKeys:
                  if isinstance(rightComp.contents[key][0], Component):
                      differentComponents[key] = ([], rightComp.contents[key])
                  else:
                      differentContentLines.append(([], rightComp.contents[key]))
  
          if len(differentContentLines) == 0 and len(differentComponents) == 0:
              return None
          else:
              left  = newFromBehavior(leftComp.name)
              right = newFromBehavior(leftComp.name)
              # add a UID, if one existed, despite the fact that they'll always be
              # the same
              uid = leftComp.getChildValue('uid')
              if uid is not None:
                  left.add( 'uid').value = uid
                  right.add('uid').value = uid
  
              for name, childPairList in differentComponents.items():
                  leftComponents, rightComponents = zip(*childPairList)
                  if len(leftComponents) > 0:
                      # filter out None
                      left.contents[name] = filter(None, leftComponents)
                  if len(rightComponents) > 0:
                      # filter out None
                      right.contents[name] = filter(None, rightComponents)
  
              for leftChildLine, rightChildLine in differentContentLines:
                  nonEmpty = leftChildLine or rightChildLine
                  name = nonEmpty[0].name
                  if leftChildLine is not None:
                      left.contents[name] = leftChildLine
                  if rightChildLine is not None:
                      right.contents[name] = rightChildLine
  
              return left, right
  
  
      vevents = processComponentLists(sortByUID(getattr(left, 'vevent_list', [])),
                                      sortByUID(getattr(right, 'vevent_list', [])))
  
      vtodos = processComponentLists(sortByUID(getattr(left, 'vtodo_list', [])),
                                     sortByUID(getattr(right, 'vtodo_list', [])))
  
      return vevents + vtodos