xref: /third_party/python/Doc/includes/dbpickle.py (revision 7db96d56)
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