17db96d56Sopenharmony_ci# Simple example presenting how persistent ID can be used to pickle 27db96d56Sopenharmony_ci# external objects by reference. 37db96d56Sopenharmony_ci 47db96d56Sopenharmony_ciimport pickle 57db96d56Sopenharmony_ciimport sqlite3 67db96d56Sopenharmony_cifrom collections import namedtuple 77db96d56Sopenharmony_ci 87db96d56Sopenharmony_ci# Simple class representing a record in our database. 97db96d56Sopenharmony_ciMemoRecord = namedtuple("MemoRecord", "key, task") 107db96d56Sopenharmony_ci 117db96d56Sopenharmony_ciclass DBPickler(pickle.Pickler): 127db96d56Sopenharmony_ci 137db96d56Sopenharmony_ci def persistent_id(self, obj): 147db96d56Sopenharmony_ci # Instead of pickling MemoRecord as a regular class instance, we emit a 157db96d56Sopenharmony_ci # persistent ID. 167db96d56Sopenharmony_ci if isinstance(obj, MemoRecord): 177db96d56Sopenharmony_ci # Here, our persistent ID is simply a tuple, containing a tag and a 187db96d56Sopenharmony_ci # key, which refers to a specific record in the database. 197db96d56Sopenharmony_ci return ("MemoRecord", obj.key) 207db96d56Sopenharmony_ci else: 217db96d56Sopenharmony_ci # If obj does not have a persistent ID, return None. This means obj 227db96d56Sopenharmony_ci # needs to be pickled as usual. 237db96d56Sopenharmony_ci return None 247db96d56Sopenharmony_ci 257db96d56Sopenharmony_ci 267db96d56Sopenharmony_ciclass DBUnpickler(pickle.Unpickler): 277db96d56Sopenharmony_ci 287db96d56Sopenharmony_ci def __init__(self, file, connection): 297db96d56Sopenharmony_ci super().__init__(file) 307db96d56Sopenharmony_ci self.connection = connection 317db96d56Sopenharmony_ci 327db96d56Sopenharmony_ci def persistent_load(self, pid): 337db96d56Sopenharmony_ci # This method is invoked whenever a persistent ID is encountered. 347db96d56Sopenharmony_ci # Here, pid is the tuple returned by DBPickler. 357db96d56Sopenharmony_ci cursor = self.connection.cursor() 367db96d56Sopenharmony_ci type_tag, key_id = pid 377db96d56Sopenharmony_ci if type_tag == "MemoRecord": 387db96d56Sopenharmony_ci # Fetch the referenced record from the database and return it. 397db96d56Sopenharmony_ci cursor.execute("SELECT * FROM memos WHERE key=?", (str(key_id),)) 407db96d56Sopenharmony_ci key, task = cursor.fetchone() 417db96d56Sopenharmony_ci return MemoRecord(key, task) 427db96d56Sopenharmony_ci else: 437db96d56Sopenharmony_ci # Always raises an error if you cannot return the correct object. 447db96d56Sopenharmony_ci # Otherwise, the unpickler will think None is the object referenced 457db96d56Sopenharmony_ci # by the persistent ID. 467db96d56Sopenharmony_ci raise pickle.UnpicklingError("unsupported persistent object") 477db96d56Sopenharmony_ci 487db96d56Sopenharmony_ci 497db96d56Sopenharmony_cidef main(): 507db96d56Sopenharmony_ci import io 517db96d56Sopenharmony_ci import pprint 527db96d56Sopenharmony_ci 537db96d56Sopenharmony_ci # Initialize and populate our database. 547db96d56Sopenharmony_ci conn = sqlite3.connect(":memory:") 557db96d56Sopenharmony_ci cursor = conn.cursor() 567db96d56Sopenharmony_ci cursor.execute("CREATE TABLE memos(key INTEGER PRIMARY KEY, task TEXT)") 577db96d56Sopenharmony_ci tasks = ( 587db96d56Sopenharmony_ci 'give food to fish', 597db96d56Sopenharmony_ci 'prepare group meeting', 607db96d56Sopenharmony_ci 'fight with a zebra', 617db96d56Sopenharmony_ci ) 627db96d56Sopenharmony_ci for task in tasks: 637db96d56Sopenharmony_ci cursor.execute("INSERT INTO memos VALUES(NULL, ?)", (task,)) 647db96d56Sopenharmony_ci 657db96d56Sopenharmony_ci # Fetch the records to be pickled. 667db96d56Sopenharmony_ci cursor.execute("SELECT * FROM memos") 677db96d56Sopenharmony_ci memos = [MemoRecord(key, task) for key, task in cursor] 687db96d56Sopenharmony_ci # Save the records using our custom DBPickler. 697db96d56Sopenharmony_ci file = io.BytesIO() 707db96d56Sopenharmony_ci DBPickler(file).dump(memos) 717db96d56Sopenharmony_ci 727db96d56Sopenharmony_ci print("Pickled records:") 737db96d56Sopenharmony_ci pprint.pprint(memos) 747db96d56Sopenharmony_ci 757db96d56Sopenharmony_ci # Update a record, just for good measure. 767db96d56Sopenharmony_ci cursor.execute("UPDATE memos SET task='learn italian' WHERE key=1") 777db96d56Sopenharmony_ci 787db96d56Sopenharmony_ci # Load the records from the pickle data stream. 797db96d56Sopenharmony_ci file.seek(0) 807db96d56Sopenharmony_ci memos = DBUnpickler(file, conn).load() 817db96d56Sopenharmony_ci 827db96d56Sopenharmony_ci print("Unpickled records:") 837db96d56Sopenharmony_ci pprint.pprint(memos) 847db96d56Sopenharmony_ci 857db96d56Sopenharmony_ci 867db96d56Sopenharmony_ciif __name__ == '__main__': 877db96d56Sopenharmony_ci main() 88