| 
									
										
										
										
											2002-11-19 22:01:02 +00:00
										 |  |  | """plistlib.py -- a tool to generate and parse MacOSX .plist files.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | The main class in this module is Plist. It takes a set of arbitrary | 
					
						
							|  |  |  | keyword arguments, which will be the top level elements of the plist | 
					
						
							|  |  |  | dictionary. After instantiation you can add more elements by assigning | 
					
						
							|  |  |  | new attributes to the Plist instance. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | To write out a plist file, call the write() method of the Plist | 
					
						
							|  |  |  | instance with a filename or a file object. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | To parse a plist from a file, use the Plist.fromFile(pathOrFile) | 
					
						
							|  |  |  | classmethod, with a file name or a file object as the only argument. | 
					
						
							|  |  |  | (Warning: you need pyexpat installed for this to work, ie. it doesn't | 
					
						
							|  |  |  | work with a vanilla Python 2.2 as shipped with MacOS X.2.) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Values can be strings, integers, floats, booleans, tuples, lists, | 
					
						
							|  |  |  | dictionaries, Data or Date objects. String values (including dictionary | 
					
						
							|  |  |  | keys) may be unicode strings -- they will be written out as UTF-8. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | For convenience, this module exports a class named Dict(), which | 
					
						
							|  |  |  | allows you to easily construct (nested) dicts using keyword arguments. | 
					
						
							|  |  |  | But regular dicts work, too. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | To support Boolean values in plists with Python < 2.3, "bool", "True" | 
					
						
							|  |  |  | and "False" are exported. Use these symbols from this module if you | 
					
						
							|  |  |  | want to be compatible with Python 2.2.x (strongly recommended). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | The <data> plist type is supported through the Data class. This is a | 
					
						
							|  |  |  | thin wrapper around a Python string. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | The <date> plist data has (limited) support through the Date class. | 
					
						
							|  |  |  | (Warning: Dates are only supported if the PyXML package is installed.) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Generate Plist example: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	pl = Plist( | 
					
						
							| 
									
										
										
										
											2002-11-20 20:47:55 +00:00
										 |  |  | 		aString="Doodah", | 
					
						
							|  |  |  | 		aList=["A", "B", 12, 32.1, [1, 2, 3]], | 
					
						
							|  |  |  | 		aFloat = 0.1, | 
					
						
							|  |  |  | 		anInt = 728, | 
					
						
							|  |  |  | 		aDict=Dict( | 
					
						
							|  |  |  | 			anotherString="<hello & hi there!>", | 
					
						
							|  |  |  | 			aUnicodeValue=u'M\xe4ssig, Ma\xdf', | 
					
						
							|  |  |  | 			aTrueValue=True, | 
					
						
							|  |  |  | 			aFalseValue=False, | 
					
						
							|  |  |  | 		), | 
					
						
							|  |  |  | 		someData = Data("<binary gunk>"), | 
					
						
							|  |  |  | 		someMoreData = Data("<lots of binary gunk>" * 10), | 
					
						
							|  |  |  | 		aDate = Date(time.mktime(time.gmtime())), | 
					
						
							| 
									
										
										
										
											2002-11-19 22:01:02 +00:00
										 |  |  | 	) | 
					
						
							|  |  |  | 	# unicode keys are possible, but a little awkward to use: | 
					
						
							|  |  |  | 	pl[u'\xc5benraa'] = "That was a unicode key." | 
					
						
							|  |  |  | 	pl.write(fileName) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Parse Plist example: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	pl = Plist.fromFile(pathOrFile) | 
					
						
							|  |  |  | 	print pl.aKey | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # written by Just van Rossum (just@letterror.com), 2002-11-19 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | __all__ = ["Plist", "Data", "Date", "Dict", "False", "True", "bool"] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | INDENT = "\t" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class DumbXMLWriter: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	def __init__(self, file): | 
					
						
							|  |  |  | 		self.file = file | 
					
						
							|  |  |  | 		self.stack = [] | 
					
						
							|  |  |  | 		self.indentLevel = 0 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	def beginElement(self, element): | 
					
						
							|  |  |  | 		self.stack.append(element) | 
					
						
							|  |  |  | 		self.writeln("<%s>" % element) | 
					
						
							|  |  |  | 		self.indentLevel += 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	def endElement(self, element): | 
					
						
							|  |  |  | 		assert self.indentLevel > 0 | 
					
						
							|  |  |  | 		assert self.stack.pop() == element | 
					
						
							|  |  |  | 		self.indentLevel -= 1 | 
					
						
							|  |  |  | 		self.writeln("</%s>" % element) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	def simpleElement(self, element, value=None): | 
					
						
							|  |  |  | 		if value: | 
					
						
							|  |  |  | 			value = _encode(value) | 
					
						
							|  |  |  | 			self.writeln("<%s>%s</%s>" % (element, value, element)) | 
					
						
							|  |  |  | 		else: | 
					
						
							|  |  |  | 			self.writeln("<%s/>" % element) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	def writeln(self, line): | 
					
						
							|  |  |  | 		if line: | 
					
						
							|  |  |  | 			self.file.write(self.indentLevel * INDENT + line + "\n") | 
					
						
							|  |  |  | 		else: | 
					
						
							|  |  |  | 			self.file.write("\n") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def _encode(text): | 
					
						
							|  |  |  | 	text = text.replace("&", "&") | 
					
						
							|  |  |  | 	text = text.replace("<", "<") | 
					
						
							|  |  |  | 	return text.encode("utf-8") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | PLISTHEADER = """\
 | 
					
						
							|  |  |  | <?xml version="1.0" encoding="UTF-8"?> | 
					
						
							|  |  |  | <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | 
					
						
							|  |  |  | """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class PlistWriter(DumbXMLWriter): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	def __init__(self, file): | 
					
						
							|  |  |  | 		file.write(PLISTHEADER) | 
					
						
							|  |  |  | 		DumbXMLWriter.__init__(self, file) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	def writeValue(self, value): | 
					
						
							|  |  |  | 		if isinstance(value, (str, unicode)): | 
					
						
							|  |  |  | 			self.simpleElement("string", value) | 
					
						
							|  |  |  | 		elif isinstance(value, bool): | 
					
						
							|  |  |  | 			# must switch for bool before int, as bool is a | 
					
						
							|  |  |  | 			# subclass of int... | 
					
						
							|  |  |  | 			if value: | 
					
						
							|  |  |  | 				self.simpleElement("true") | 
					
						
							|  |  |  | 			else: | 
					
						
							|  |  |  | 				self.simpleElement("false") | 
					
						
							|  |  |  | 		elif isinstance(value, int): | 
					
						
							|  |  |  | 			self.simpleElement("integer", str(value)) | 
					
						
							|  |  |  | 		elif isinstance(value, float): | 
					
						
							|  |  |  | 			# should perhaps use repr() for better precision? | 
					
						
							|  |  |  | 			self.simpleElement("real", str(value)) | 
					
						
							|  |  |  | 		elif isinstance(value, (dict, Dict)): | 
					
						
							|  |  |  | 			self.writeDict(value) | 
					
						
							|  |  |  | 		elif isinstance(value, Data): | 
					
						
							|  |  |  | 			self.writeData(value) | 
					
						
							|  |  |  | 		elif isinstance(value, Date): | 
					
						
							|  |  |  | 			self.simpleElement("date", value.toString()) | 
					
						
							|  |  |  | 		elif isinstance(value, (tuple, list)): | 
					
						
							|  |  |  | 			self.writeArray(value) | 
					
						
							|  |  |  | 		else: | 
					
						
							|  |  |  | 			assert 0, "unsuported type: %s" % type(value) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	def writeData(self, data): | 
					
						
							|  |  |  | 		self.beginElement("data") | 
					
						
							|  |  |  | 		for line in data.asBase64().split("\n"): | 
					
						
							|  |  |  | 			if line: | 
					
						
							| 
									
										
										
										
											2002-11-19 23:11:49 +00:00
										 |  |  | 				self.writeln(line) | 
					
						
							| 
									
										
										
										
											2002-11-19 22:01:02 +00:00
										 |  |  | 		self.endElement("data") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	def writeDict(self, d): | 
					
						
							|  |  |  | 		self.beginElement("dict") | 
					
						
							|  |  |  | 		items = d.items() | 
					
						
							|  |  |  | 		items.sort() | 
					
						
							|  |  |  | 		for key, value in items: | 
					
						
							|  |  |  | 			assert isinstance(key, (str, unicode)), "keys must be strings" | 
					
						
							|  |  |  | 			self.simpleElement("key", key) | 
					
						
							|  |  |  | 			self.writeValue(value) | 
					
						
							|  |  |  | 		self.endElement("dict") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	def writeArray(self, array): | 
					
						
							|  |  |  | 		self.beginElement("array") | 
					
						
							|  |  |  | 		for value in array: | 
					
						
							|  |  |  | 			self.writeValue(value) | 
					
						
							|  |  |  | 		self.endElement("array") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class Dict: | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-11-20 20:47:55 +00:00
										 |  |  | 	"""Dict wrapper for convenient access of values through attributes.""" | 
					
						
							| 
									
										
										
										
											2002-11-19 22:01:02 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-11-21 10:29:57 +00:00
										 |  |  | 	def __init__(self, **kwargs): | 
					
						
							|  |  |  | 		self.__dict__.update(kwargs) | 
					
						
							| 
									
										
										
										
											2002-11-19 22:01:02 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	def __cmp__(self, other): | 
					
						
							|  |  |  | 		if isinstance(other, self.__class__): | 
					
						
							|  |  |  | 			return cmp(self.__dict__, other.__dict__) | 
					
						
							|  |  |  | 		elif isinstance(other, dict): | 
					
						
							|  |  |  | 			return cmp(self.__dict__, other) | 
					
						
							|  |  |  | 		else: | 
					
						
							|  |  |  | 			return cmp(id(self), id(other)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	def __str__(self): | 
					
						
							|  |  |  | 		return "%s(**%s)" % (self.__class__.__name__, self.__dict__) | 
					
						
							|  |  |  | 	__repr__ = __str__ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-11-20 20:47:55 +00:00
										 |  |  | 	def copy(self): | 
					
						
							|  |  |  | 		return self.__class__(**self.__dict__) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-11-19 22:01:02 +00:00
										 |  |  | 	def __getattr__(self, attr): | 
					
						
							|  |  |  | 		"""Delegate everything else to the dict object.""" | 
					
						
							|  |  |  | 		return getattr(self.__dict__, attr) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class Plist(Dict): | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-11-19 23:11:49 +00:00
										 |  |  | 	"""The main Plist object. Basically a dict (the toplevel object
 | 
					
						
							|  |  |  | 	of a plist is a dict) with two additional methods to read from | 
					
						
							|  |  |  | 	and write to files. | 
					
						
							|  |  |  | 	"""
 | 
					
						
							| 
									
										
										
										
											2002-11-19 22:01:02 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	def fromFile(cls, pathOrFile): | 
					
						
							|  |  |  | 		didOpen = 0 | 
					
						
							|  |  |  | 		if not hasattr(pathOrFile, "write"): | 
					
						
							|  |  |  | 			pathOrFile = open(pathOrFile) | 
					
						
							|  |  |  | 			didOpen = 1 | 
					
						
							|  |  |  | 		p = PlistParser() | 
					
						
							|  |  |  | 		plist = p.parse(pathOrFile) | 
					
						
							|  |  |  | 		if didOpen: | 
					
						
							|  |  |  | 			pathOrFile.close() | 
					
						
							|  |  |  | 		return plist | 
					
						
							|  |  |  | 	fromFile = classmethod(fromFile) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	def write(self, pathOrFile): | 
					
						
							|  |  |  | 		if not hasattr(pathOrFile, "write"): | 
					
						
							|  |  |  | 			pathOrFile = open(pathOrFile, "w") | 
					
						
							|  |  |  | 			didOpen = 1 | 
					
						
							|  |  |  | 		else: | 
					
						
							|  |  |  | 			didOpen = 0 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		writer = PlistWriter(pathOrFile) | 
					
						
							|  |  |  | 		writer.writeln("<plist version=\"1.0\">") | 
					
						
							|  |  |  | 		writer.writeDict(self.__dict__) | 
					
						
							|  |  |  | 		writer.writeln("</plist>") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if didOpen: | 
					
						
							|  |  |  | 			pathOrFile.close() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class Data: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"""Wrapper for binary data.""" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	def __init__(self, data): | 
					
						
							|  |  |  | 		self.data = data | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	def fromBase64(cls, data): | 
					
						
							|  |  |  | 		import base64 | 
					
						
							|  |  |  | 		return cls(base64.decodestring(data)) | 
					
						
							|  |  |  | 	fromBase64 = classmethod(fromBase64) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	def asBase64(self): | 
					
						
							|  |  |  | 		import base64 | 
					
						
							|  |  |  | 		return base64.encodestring(self.data) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	def __cmp__(self, other): | 
					
						
							|  |  |  | 		if isinstance(other, self.__class__): | 
					
						
							|  |  |  | 			return cmp(self.data, other.data) | 
					
						
							|  |  |  | 		elif isinstance(other, str): | 
					
						
							|  |  |  | 			return cmp(self.data, other) | 
					
						
							|  |  |  | 		else: | 
					
						
							|  |  |  | 			return cmp(id(self), id(other)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	def __repr__(self): | 
					
						
							|  |  |  | 		return "%s(%s)" % (self.__class__.__name__, repr(self.data)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class Date: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"""Primitive date wrapper, uses time floats internally, is agnostic
 | 
					
						
							|  |  |  | 	about time zones. | 
					
						
							|  |  |  | 	"""
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	def __init__(self, date): | 
					
						
							|  |  |  | 		if isinstance(date, str): | 
					
						
							|  |  |  | 			from xml.utils.iso8601 import parse | 
					
						
							|  |  |  | 			date = parse(date) | 
					
						
							|  |  |  | 		self.date = date | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	def toString(self): | 
					
						
							|  |  |  | 		from xml.utils.iso8601 import tostring | 
					
						
							|  |  |  | 		return tostring(self.date) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	def __cmp__(self, other): | 
					
						
							|  |  |  | 		if isinstance(other, self.__class__): | 
					
						
							|  |  |  | 			return cmp(self.date, other.date) | 
					
						
							|  |  |  | 		elif isinstance(other, (int, float)): | 
					
						
							|  |  |  | 			return cmp(self.date, other) | 
					
						
							|  |  |  | 		else: | 
					
						
							|  |  |  | 			return cmp(id(self), id(other)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	def __repr__(self): | 
					
						
							|  |  |  | 		return "%s(%s)" % (self.__class__.__name__, repr(self.toString())) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class PlistParser: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	def __init__(self): | 
					
						
							|  |  |  | 		self.stack = [] | 
					
						
							|  |  |  | 		self.currentKey = None | 
					
						
							|  |  |  | 		self.root = None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	def parse(self, file): | 
					
						
							|  |  |  | 		from xml.parsers.expat import ParserCreate | 
					
						
							|  |  |  | 		parser = ParserCreate() | 
					
						
							|  |  |  | 		parser.StartElementHandler = self.handleBeginElement | 
					
						
							|  |  |  | 		parser.EndElementHandler = self.handleEndElement | 
					
						
							|  |  |  | 		parser.CharacterDataHandler = self.handleData | 
					
						
							|  |  |  | 		parser.ParseFile(file) | 
					
						
							|  |  |  | 		return self.root | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	def handleBeginElement(self, element, attrs): | 
					
						
							|  |  |  | 		self.data = [] | 
					
						
							|  |  |  | 		handler = getattr(self, "begin_" + element, None) | 
					
						
							|  |  |  | 		if handler is not None: | 
					
						
							|  |  |  | 			handler(attrs) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	def handleEndElement(self, element): | 
					
						
							|  |  |  | 		handler = getattr(self, "end_" + element, None) | 
					
						
							|  |  |  | 		if handler is not None: | 
					
						
							|  |  |  | 			handler() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	def handleData(self, data): | 
					
						
							|  |  |  | 		self.data.append(data) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	def addObject(self, value): | 
					
						
							|  |  |  | 		if self.currentKey is not None: | 
					
						
							|  |  |  | 			self.stack[-1][self.currentKey] = value | 
					
						
							|  |  |  | 			self.currentKey = None | 
					
						
							|  |  |  | 		elif not self.stack: | 
					
						
							|  |  |  | 			# this is the root object | 
					
						
							|  |  |  | 			assert self.root is value | 
					
						
							|  |  |  | 		else: | 
					
						
							|  |  |  | 			self.stack[-1].append(value) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	def getData(self): | 
					
						
							|  |  |  | 		data = "".join(self.data) | 
					
						
							|  |  |  | 		try: | 
					
						
							|  |  |  | 			data = data.encode("ascii") | 
					
						
							|  |  |  | 		except UnicodeError: | 
					
						
							|  |  |  | 			pass | 
					
						
							|  |  |  | 		self.data = [] | 
					
						
							|  |  |  | 		return data | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	# element handlers | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	def begin_dict(self, attrs): | 
					
						
							|  |  |  | 		if self.root is None: | 
					
						
							|  |  |  | 			self.root = d = Plist() | 
					
						
							|  |  |  | 		else: | 
					
						
							|  |  |  | 			d = Dict() | 
					
						
							|  |  |  | 		self.addObject(d) | 
					
						
							|  |  |  | 		self.stack.append(d) | 
					
						
							|  |  |  | 	def end_dict(self): | 
					
						
							|  |  |  | 		self.stack.pop() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	def end_key(self): | 
					
						
							|  |  |  | 		self.currentKey = self.getData() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	def begin_array(self, attrs): | 
					
						
							|  |  |  | 		a = [] | 
					
						
							|  |  |  | 		self.addObject(a) | 
					
						
							|  |  |  | 		self.stack.append(a) | 
					
						
							|  |  |  | 	def end_array(self): | 
					
						
							|  |  |  | 		self.stack.pop() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	def end_true(self): | 
					
						
							|  |  |  | 		self.addObject(True) | 
					
						
							|  |  |  | 	def end_false(self): | 
					
						
							|  |  |  | 		self.addObject(False) | 
					
						
							|  |  |  | 	def end_integer(self): | 
					
						
							|  |  |  | 		self.addObject(int(self.getData())) | 
					
						
							|  |  |  | 	def end_real(self): | 
					
						
							|  |  |  | 		self.addObject(float(self.getData())) | 
					
						
							|  |  |  | 	def end_string(self): | 
					
						
							|  |  |  | 		self.addObject(self.getData()) | 
					
						
							|  |  |  | 	def end_data(self): | 
					
						
							|  |  |  | 		self.addObject(Data.fromBase64(self.getData())) | 
					
						
							|  |  |  | 	def end_date(self): | 
					
						
							|  |  |  | 		self.addObject(Date(self.getData())) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # cruft to support booleans in Python <= 2.3 | 
					
						
							|  |  |  | import sys | 
					
						
							|  |  |  | if sys.version_info[:2] < (2, 3): | 
					
						
							|  |  |  | 	# Python 2.2 and earlier: no booleans | 
					
						
							|  |  |  | 	# Python 2.2.x: booleans are ints | 
					
						
							|  |  |  | 	class bool(int): | 
					
						
							|  |  |  | 		"""Imitation of the Python 2.3 bool object.""" | 
					
						
							|  |  |  | 		def __new__(cls, value): | 
					
						
							|  |  |  | 			return int.__new__(cls, not not value) | 
					
						
							|  |  |  | 		def __repr__(self): | 
					
						
							|  |  |  | 			if self: | 
					
						
							|  |  |  | 				return "True" | 
					
						
							|  |  |  | 			else: | 
					
						
							|  |  |  | 				return "False" | 
					
						
							|  |  |  | 	True = bool(1) | 
					
						
							|  |  |  | 	False = bool(0) | 
					
						
							|  |  |  | else: | 
					
						
							| 
									
										
										
										
											2002-11-19 23:01:20 +00:00
										 |  |  | 	# Bind the boolean builtins to local names | 
					
						
							|  |  |  | 	True = True | 
					
						
							|  |  |  | 	False = False | 
					
						
							|  |  |  | 	bool = bool | 
					
						
							| 
									
										
										
										
											2002-11-19 22:01:02 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | if __name__ == "__main__": | 
					
						
							|  |  |  | 	from StringIO import StringIO | 
					
						
							|  |  |  | 	import time | 
					
						
							|  |  |  | 	if len(sys.argv) == 1: | 
					
						
							|  |  |  | 		pl = Plist( | 
					
						
							| 
									
										
										
										
											2002-11-20 20:47:55 +00:00
										 |  |  | 			aString="Doodah", | 
					
						
							|  |  |  | 			aList=["A", "B", 12, 32.1, [1, 2, 3]], | 
					
						
							|  |  |  | 			aFloat = 0.1, | 
					
						
							|  |  |  | 			anInt = 728, | 
					
						
							|  |  |  | 			aDict=Dict( | 
					
						
							|  |  |  | 				anotherString="<hello & hi there!>", | 
					
						
							|  |  |  | 				aUnicodeValue=u'M\xe4ssig, Ma\xdf', | 
					
						
							|  |  |  | 				aTrueValue=True, | 
					
						
							|  |  |  | 				aFalseValue=False, | 
					
						
							|  |  |  | 			), | 
					
						
							|  |  |  | 			someData = Data("<binary gunk>"), | 
					
						
							|  |  |  | 			someMoreData = Data("<lots of binary gunk>" * 10), | 
					
						
							|  |  |  | 			aDate = Date(time.mktime(time.gmtime())), | 
					
						
							| 
									
										
										
										
											2002-11-19 22:01:02 +00:00
										 |  |  | 		) | 
					
						
							|  |  |  | 	elif len(sys.argv) == 2: | 
					
						
							|  |  |  | 		pl = Plist.fromFile(sys.argv[1]) | 
					
						
							|  |  |  | 	else: | 
					
						
							|  |  |  | 		print "Too many arguments: at most 1 plist file can be given." | 
					
						
							|  |  |  | 		sys.exit(1) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	# unicode keys are possible, but a little awkward to use: | 
					
						
							|  |  |  | 	pl[u'\xc5benraa'] = "That was a unicode key." | 
					
						
							|  |  |  | 	f = StringIO() | 
					
						
							|  |  |  | 	pl.write(f) | 
					
						
							|  |  |  | 	xml = f.getvalue() | 
					
						
							|  |  |  | 	print xml | 
					
						
							|  |  |  | 	f.seek(0) | 
					
						
							|  |  |  | 	pl2 = Plist.fromFile(f) | 
					
						
							|  |  |  | 	assert pl == pl2 | 
					
						
							|  |  |  | 	f = StringIO() | 
					
						
							|  |  |  | 	pl2.write(f) | 
					
						
							|  |  |  | 	assert xml == f.getvalue() | 
					
						
							|  |  |  | 	#print repr(pl2) |