| 
									
										
										
										
											2008-10-18 20:47:58 +00:00
										 |  |  | # Simple example presenting how persistent ID can be used to pickle | 
					
						
							|  |  |  | # external objects by reference. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import pickle | 
					
						
							|  |  |  | import sqlite3 | 
					
						
							|  |  |  | from collections import namedtuple | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # Simple class representing a record in our database. | 
					
						
							|  |  |  | MemoRecord = namedtuple("MemoRecord", "key, task") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class DBPickler(pickle.Pickler): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def persistent_id(self, obj): | 
					
						
							|  |  |  |         # Instead of pickling MemoRecord as a regular class instance, we emit a | 
					
						
							| 
									
										
										
										
											2008-10-25 17:10:07 +00:00
										 |  |  |         # persistent ID. | 
					
						
							| 
									
										
										
										
											2008-10-18 20:47:58 +00:00
										 |  |  |         if isinstance(obj, MemoRecord): | 
					
						
							| 
									
										
										
										
											2008-10-25 17:10:07 +00:00
										 |  |  |             # Here, our persistent ID is simply a tuple, containing a tag and a | 
					
						
							|  |  |  |             # key, which refers to a specific record in the database. | 
					
						
							| 
									
										
										
										
											2008-10-18 20:47:58 +00:00
										 |  |  |             return ("MemoRecord", obj.key) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             # If obj does not have a persistent ID, return None. This means obj | 
					
						
							|  |  |  |             # needs to be pickled as usual. | 
					
						
							|  |  |  |             return None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class DBUnpickler(pickle.Unpickler): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, file, connection): | 
					
						
							|  |  |  |         super().__init__(file) | 
					
						
							|  |  |  |         self.connection = connection | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def persistent_load(self, pid): | 
					
						
							|  |  |  |         # This method is invoked whenever a persistent ID is encountered. | 
					
						
							|  |  |  |         # Here, pid is the tuple returned by DBPickler. | 
					
						
							|  |  |  |         cursor = self.connection.cursor() | 
					
						
							|  |  |  |         type_tag, key_id = pid | 
					
						
							|  |  |  |         if type_tag == "MemoRecord": | 
					
						
							|  |  |  |             # Fetch the referenced record from the database and return it. | 
					
						
							|  |  |  |             cursor.execute("SELECT * FROM memos WHERE key=?", (str(key_id),)) | 
					
						
							|  |  |  |             key, task = cursor.fetchone() | 
					
						
							|  |  |  |             return MemoRecord(key, task) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             # Always raises an error if you cannot return the correct object. | 
					
						
							|  |  |  |             # Otherwise, the unpickler will think None is the object referenced | 
					
						
							|  |  |  |             # by the persistent ID. | 
					
						
							|  |  |  |             raise pickle.UnpicklingError("unsupported persistent object") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-10-24 01:32:40 +00:00
										 |  |  | def main(): | 
					
						
							| 
									
										
										
										
											2011-04-26 13:55:55 -07:00
										 |  |  |     import io | 
					
						
							|  |  |  |     import pprint | 
					
						
							| 
									
										
										
										
											2008-10-18 20:47:58 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # Initialize and populate our database. | 
					
						
							|  |  |  |     conn = sqlite3.connect(":memory:") | 
					
						
							|  |  |  |     cursor = conn.cursor() | 
					
						
							|  |  |  |     cursor.execute("CREATE TABLE memos(key INTEGER PRIMARY KEY, task TEXT)") | 
					
						
							|  |  |  |     tasks = ( | 
					
						
							|  |  |  |         'give food to fish', | 
					
						
							|  |  |  |         'prepare group meeting', | 
					
						
							|  |  |  |         'fight with a zebra', | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |     for task in tasks: | 
					
						
							|  |  |  |         cursor.execute("INSERT INTO memos VALUES(NULL, ?)", (task,)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Fetch the records to be pickled. | 
					
						
							|  |  |  |     cursor.execute("SELECT * FROM memos") | 
					
						
							|  |  |  |     memos = [MemoRecord(key, task) for key, task in cursor] | 
					
						
							|  |  |  |     # Save the records using our custom DBPickler. | 
					
						
							|  |  |  |     file = io.BytesIO() | 
					
						
							|  |  |  |     DBPickler(file).dump(memos) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-10-24 01:32:40 +00:00
										 |  |  |     print("Pickled records:") | 
					
						
							|  |  |  |     pprint.pprint(memos) | 
					
						
							| 
									
										
										
										
											2008-10-18 20:47:58 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # Update a record, just for good measure. | 
					
						
							|  |  |  |     cursor.execute("UPDATE memos SET task='learn italian' WHERE key=1") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-10-24 01:32:40 +00:00
										 |  |  |     # Load the records from the pickle data stream. | 
					
						
							| 
									
										
										
										
											2008-10-18 20:47:58 +00:00
										 |  |  |     file.seek(0) | 
					
						
							|  |  |  |     memos = DBUnpickler(file, conn).load() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-10-24 01:32:40 +00:00
										 |  |  |     print("Unpickled records:") | 
					
						
							|  |  |  |     pprint.pprint(memos) | 
					
						
							| 
									
										
										
										
											2008-10-18 20:47:58 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | if __name__ == '__main__': | 
					
						
							|  |  |  |     main() |