{ "info": { "author": "Shane Hathaway and the Zope Community", "author_email": "zope-dev@zope.org", "bugtrack_url": null, "classifiers": [], "description": "Overview\n--------\n\nThe keas.pbpersist package provides a way to store ZODB objects using\nGoogle Protocol Buffer encoding rather than pickling. It requires the\nkeas.pbstate package. The Protocol Buffer format is more limited than\npickles, but it is also simpler, better documented, and not dependent\non any particular programming language.\n\nOnly Persistent objects that use the ProtobufState metaclass\n(from the keas.pbstate.meta module) are eligible\nfor this kind of serialization, so applications that want to take advantage\nof keas.pbpersist need to be refactored at the model level. However,\napplications can be refactored gradually, since protobuf\nencoded objects and pickled objects can coexist and hold references\nto each other within a single database.\n\nThis package is expected to be fully compatible with most ZODB\nstorages, including FileStorage, ZEO, RelStorage, and any other storage\nthat treats object records as opaque binary streams.\n\nNote that this package requires a patch to ZODB; see the \"-polling-serial\"\npatched version at the following site:\n\n http://packages.willowrise.org\n\n\nHow to Use This Package\n-----------------------\n\nThe documentation below is in doctest format, meaning that it serves\nas both documentation and tests. You can try out the code in a Python\ninterpreter session.\n\nRegister the protobuf serializer with ZODB. This only needs to happen\nonce per application run, but it does no harm if it happens more than once.\n\n >>> from keas.pbpersist.pbformat import register\n >>> register()\n >>> register()\n\nPContact is a simple Persistent class that stores its state in a protobuf\nmessage. See the source code; it's quite simple. For this test, create\nan instance an fill out the required field.\n\n >>> from keas.pbpersist.tests import PContact\n >>> bob = PContact()\n >>> bob.name = u'Bob'\n\nSet up an in-memory object database and put the PContact in it. Of course,\nif you already have a database, you can instead attach the object to some\nobject in your own database.\n\n >>> from ZODB.DemoStorage import DemoStorage\n >>> from ZODB.DB import DB\n >>> import transaction\n >>> storage = DemoStorage()\n >>> db = DB(storage, database_name='main')\n >>> conn1 = db.open()\n >>> conn1.root()['bob'] = bob\n >>> transaction.commit()\n\nLet's take a peek at what got stored in the database. Look Ma, no pickles!\nThe {protobuf} prefix tells ZODB to use code in the keas.pbpersist package\nto deserialize this object.\n\n >>> data, serial = storage.load(bob._p_oid, '')\n >>> data\n '{protobuf}\\n \\n\\x14keas.pbpersist.tests\\x12\\x08PContact\\x12\\x07\\x08\\x01\\x12\\x03Bob'\n\nHere is a demonstration of how to crack open that record using nothing but\nlanguage independent operations. The ObjectRecord message holds the object's\nclass, state, and references; the state field holds a class-specific\nprotobuf message.\n\n >>> from keas.pbpersist.persistent_pb2 import ObjectRecord\n >>> rec = ObjectRecord()\n >>> rec.MergeFromString(data[10:]) # skip the {protobuf} prefix\n 43\n >>> rec.class_meta.module_name\n u'keas.pbpersist.tests'\n >>> rec.class_meta.class_name\n u'PContact'\n >>> len(rec.references)\n 0\n >>> pbuf = PContact.protobuf_type()\n >>> pbuf.MergeFromString(rec.state)\n 7\n >>> pbuf.name\n u'Bob'\n\nYou could easily emulate that code in C++ or Java, the other two languages\nsupported by Protocol Buffers at this time. However, that was a\nnoisy, complex operation. ZODB makes reading objects dramatically simpler.\n\n >>> conn2 = db.open()\n >>> bob2 = conn2.root()['bob']\n >>> bob2.name\n u'Bob'\n >>> conn2.close()\n\n\nReferences\n----------\n\nCreate another PContact and assign that contact to be a guardian for Bob.\nThe add() method below is part of Google's protobuf API. The\nprotobuf_refs attribute is provided by the ProtobufState metaclass\nin keas.pbstate.\n\n >>> alice = PContact()\n >>> alice.name = u'Alice'\n >>> ref = bob.guardians.add()\n >>> bob.protobuf_refs.set(ref, alice)\n >>> transaction.commit()\n\n[Ed: I sure don't like the way we manage references right now. I'd much\nrather see \"bob.guardians.append(alice)\", but that is not yet possible.]\n\nNow let's see what happens if we get Alice through Bob in a different\nZODB connection.\n\n >>> conn2 = db.open()\n >>> bob2 = conn2.root()['bob']\n >>> alice2 = bob2.protobuf_refs.get(bob2.guardians[0])\n >>> alice2.name\n u'Alice'\n\nNote that alice2 is a duplicate of alice, not the same object, because\nthe two come from different ZODB connections.\n\n >>> alice is alice2\n False\n\nWe also have the option of referring to objects by OID, but that\nis rarely necessary.\n\n >>> conn3 = db.open()\n >>> bob3 = conn3[bob._p_oid]\n\nZODB loads all objects lazily, including objects serialized using\nthis package. When a persistent object's _p_changed\nattribute is None, the object is in the \"ghost\" state; it does not\nyet contain data. Accessing an attribute transparently loads the object\nstate.\n\n >>> bob3._p_changed is None\n True\n >>> bob3.name\n u'Bob'\n >>> bob3._p_changed\n False\n\nClean up the extra connections.\n \n >>> conn3.close()\n >>> conn2.close()\n\n\nPacking\n-------\n\nStorages need to be able to follow chains of references in order to pack,\nso let's make sure the referencesf() function still does what it should.\n\n >>> from ZODB.serialize import referencesf\n >>> data, serial = storage.load(conn1.root()._p_oid, '')\n >>> referencesf(data) == [bob._p_oid]\n True\n >>> data, serial = storage.load(bob._p_oid, '')\n >>> referencesf(data) == [alice._p_oid]\n True\n >>> data, serial = storage.load(alice._p_oid, '')\n >>> referencesf(data)\n []\n\n\nReference Capabilities And Limitations\n---------------------------------------\n\nObjects serialized using this package can refer to any other Persistent\nobjects. (Note that we already demonstrated that pickled objects\ncan refer to protobuf serialized objects when we added \"bob\" to the\nroot object in the database.)\n\n >>> from persistent.mapping import PersistentMapping\n >>> len(bob.guardians)\n 1\n >>> ref = bob.guardians.add()\n >>> bob.protobuf_refs.set(ref, PersistentMapping())\n >>> transaction.commit()\n >>> len(bob.guardians)\n 2\n\nHowever, objects serialized using this package can not hold a reference\nto non-Persistent objects. (This is a limitation of keas.pbpersist, but\nnot a limitation of keas.pbstate, which does not depend on ZODB.) We don't\ndetect bad references until the first phase of transaction commit.\n\n >>> len(bob.guardians)\n 2\n >>> ref = bob.guardians.add()\n >>> bob.protobuf_refs.set(ref, {})\n >>> len(bob.guardians)\n 3\n >>> transaction.commit()\n Traceback (most recent call last):\n ...\n POSError: Protobuf reference target is not a Persistent object: {}\n\nAborting the transaction undoes the damage.\n\n >>> transaction.abort()\n >>> len(bob.guardians)\n 2\n\nWe can make weak references, though!\n\n >>> from persistent.wref import WeakRef\n >>> ref = bob.guardians.add()\n >>> bob.protobuf_refs.set(ref, WeakRef(PersistentMapping()))\n >>> transaction.commit()\n >>> len(bob.guardians)\n 3\n\nWe can make cross-database references.\n\n >>> storage_beta = DemoStorage()\n >>> db_beta = DB(storage_beta, database_name='beta', databases=db.databases)\n >>> root_beta = conn1.get_connection('beta').root()\n >>> root_beta['obj'] = PersistentMapping()\n >>> transaction.commit()\n >>> ref = bob.guardians.add()\n >>> bob.protobuf_refs.set(ref, root_beta['obj'])\n >>> transaction.commit()\n\nWe can make a reference to an instance of a class with a __getnewargs__\nmethod. ZODB does not store the class metadata in references to instances\nof such classes.\n\n >>> from keas.pbpersist.tests import Adder\n >>> adder = Adder(7)\n >>> conn1.root()['adder'] = adder\n >>> ref = bob.guardians.add()\n >>> bob.protobuf_refs.set(ref, adder)\n >>> transaction.commit()\n\nWe can make a reference to an instance of a class with a __getnewargs__\nmethod, even when that instance is in another database.\n\n >>> adder = Adder(6)\n >>> root_beta['adder'] = adder\n >>> transaction.commit()\n >>> ref = bob.guardians.add()\n >>> bob.protobuf_refs.set(ref, adder)\n >>> transaction.commit()\n\nJust to be sure all those references work, access all of them in another\nconnection.\n\n >>> conn2 = db.open()\n >>> bob2 = conn2.root()['bob']\n >>> len(bob2.guardians)\n 6\n\n >>> bob2.protobuf_refs.get(bob2.guardians[0])\n \n\n >>> bob2.protobuf_refs.get(bob2.guardians[1])\n {}\n >>> bob2.protobuf_refs.get(bob2.guardians[1])._p_jar is conn2\n True\n\n >>> bob2.protobuf_refs.get(bob2.guardians[2])\n \n >>> bob2.protobuf_refs.get(bob2.guardians[2])()\n {}\n\n >>> bob2.protobuf_refs.get(bob2.guardians[3])\n {}\n >>> bob2.protobuf_refs.get(bob2.guardians[3])._p_jar is conn2\n False\n\n >>> bob2.protobuf_refs.get(bob2.guardians[4])\n \n >>> bob2.protobuf_refs.get(bob2.guardians[4]).add(3)\n 10\n\n >>> bob2.protobuf_refs.get(bob2.guardians[5])\n \n >>> bob2.protobuf_refs.get(bob2.guardians[5]).add(3)\n 9\n\n >>> conn2.close()\n\n\nEdge Cases\n----------\n\nWe can't currently store instances of a ProtobufState Persistent class that\nimplements __getnewargs__().\n\n >>> from keas.pbpersist.tests import PContactWithGetNewArgs\n >>> fail_contact = PContactWithGetNewArgs()\n >>> fail_contact.name = u'Loser'\n >>> conn1.root()['fail_contact'] = fail_contact\n >>> transaction.commit()\n Traceback (most recent call last):\n ...\n POSError: ProtobufSerializer can not serialize classes using __getnewargs__ or __getinitargs__\n\n\nClean Up\n--------\n\nThe tests are finished. Close the object database.\n\n >>> transaction.abort()\n >>> conn1.close()\n >>> db.close()", "description_content_type": null, "docs_url": null, "download_url": "UNKNOWN", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "UNKNOWN", "keywords": null, "license": "ZPL 2.1", "maintainer": null, "maintainer_email": null, "name": "keas.pbpersist", "package_url": "https://pypi.org/project/keas.pbpersist/", "platform": "UNKNOWN", "project_url": "https://pypi.org/project/keas.pbpersist/", "project_urls": { "Download": "UNKNOWN", "Homepage": "UNKNOWN" }, "release_url": "https://pypi.org/project/keas.pbpersist/0.1/", "requires_dist": null, "requires_python": null, "summary": "ZODB Persistence in a Google Protocol Buffer", "version": "0.1" }, "last_serial": 683102, "releases": { "0.1": [ { "comment_text": "", "digests": { "md5": "149258f233a9a4c9568366895ba1df2c", "sha256": "3a8f738c31b0655c8e862cc7c60eb88d4193c002a4b7749d28ac099054213f12" }, "downloads": -1, "filename": "keas.pbpersist-0.1.tar.gz", "has_sig": false, "md5_digest": "149258f233a9a4c9568366895ba1df2c", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 10565, "upload_time": "2009-01-28T03:52:27", "url": "https://files.pythonhosted.org/packages/9b/5f/8da178b3e5e67b77d2cd47cfe3488f2a7929a8af120332f0468c77a51ee5/keas.pbpersist-0.1.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "149258f233a9a4c9568366895ba1df2c", "sha256": "3a8f738c31b0655c8e862cc7c60eb88d4193c002a4b7749d28ac099054213f12" }, "downloads": -1, "filename": "keas.pbpersist-0.1.tar.gz", "has_sig": false, "md5_digest": "149258f233a9a4c9568366895ba1df2c", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 10565, "upload_time": "2009-01-28T03:52:27", "url": "https://files.pythonhosted.org/packages/9b/5f/8da178b3e5e67b77d2cd47cfe3488f2a7929a8af120332f0468c77a51ee5/keas.pbpersist-0.1.tar.gz" } ] }