{ "info": { "author": "Brent Tubbs", "author_email": "brent.tubbs@gmail.com", "bugtrack_url": null, "classifiers": [], "description": "========\npgpubsub\n========\n\npgpubsub provides convenient access to the `event notification system`_ built\ninto the PostgreSQL_ database. This provides a real-time Pub/Sub system similar\nto the one in Redis_.\n\nUsage\n=====\n\nFirst you need to make a connection::\n\n import pgpubsub\n pubsub = pgpubsub.connect(user='postgres', database='test')\n\nThe arguments accepted by the pgpubsub.connect() function are identical to those\nsupported by the `psycopg2.connect()`_ function.\n\nSending Events\n--------------\n\nTo send an event, use the pubsub.notify() method::\n\n pubsub.notify('test_channel', 'some message')\n\nReceiving Events\n----------------\n\nTo receive events, you must first subscribe to a specific channel with the\npubsub.listen() method::\n\n pubsub.listen('test')\n\nYou can call pubsub.listen() multiple times to receive events from multiple\nchannels::\n\n pubsub.listen('chan1')\n pubsub.listen('chan2')\n\nNote that channel names must be a valid `SQL identifiers`_. To quote from the\nPostgres docs:\n\n SQL identifiers and key words must begin with a letter (a-z, but also letters\n with diacritical marks and non-Latin letters) or an underscore (_). Subsequent\n characters in an identifier or key word can be letters, underscores, digits\n (0-9), or dollar signs ($).\n\nWARNING\n~~~~~~~\n\nBecause channels are SQL identifiers rather than strings, they can't be\nquoted/escaped by Psycopg2 like strings can. It is not safe to build a channel\nname from untrusted user input.\n\nDO NOT DO THIS::\n\n channel = 'events_' + username\n pubsub.listen(channel)\n\nIf you do, then your whole database could be destroyed by someone with the\nusername \"; DROP TABLE users;\". `Mandatory XKCD`_.\n\nOnce you have subscribed to one or more channels, you can choose to receive\nevents either by iterating over pubsub.events(), or by repeatedly calling the\npubsub.get_event() method.\n\npubsub.events()\n~~~~~~~~~~~~~~~\n\nThis is a generator over the stream of events coming on the pubsub. It lets you\nloop over the events just as you would a list::\n\n for e in pubsub.events():\n print e.payload\n\nBehind the scenes, the pubsub is blocking on the standard library's\nselect.select_ function. You can provide two additional arguments to\npubsub.events() to control how timeouts are handled when waiting on\nselect.select:\n\n- select_timeout: The number of seconds to wait on select.select before giving\n up and trying again. Defaults to 5.\n- yield_timeouts: This defaults to False. If set to True, then pubsub.events()\n will yield a None each time you go for select_timeout seconds before receiving\n an event. This is useful for things like WebSockets where you may want to\n send a keepalive message even if no new data has been received::\n\n for e in pubsub.events(yield_timeouts=True):\n if e is None:\n send_websocket_ping()\n else:\n send_websocket_message(e.payload)\n\npubsub.get_event()\n~~~~~~~~~~~~~~~~~~\n\nThis method always returns immediately. If an event has been received, it will\nreturn that event. If no event has been received, it will return None.\n\nIf multiple events have been received and are waiting in the queue, then\nrepeated get_event() calls will keep returning the next event until there aren't\nany left and it returns None::\n\n >>> pubsub.listen('test')\n >>> pubsub.get_event() # Nothing delivered yet, so returns None\n >>> pubsub.notify('test', 'message 1')\n >>> pubsub.notify('test', 'message 2')\n >>> pubsub.get_event()\n Notify(9425, 'test', 'message 1')\n >>> pubsub.get_event()\n Notify(9425, 'test', 'message 2')\n >>> pubsub.get_event() # No more messages, so returns None\n\nThe pubsub.get_event() method is intended for integration into event loops where\nblocking on pubsub.events() would cause problems.\n\nUnsubscribing\n-------------\n\nIf you want to stop receiving events on one of the channels you're currently\nsubscribed to, you can call pubsub.unlisten()::\n\n pubsub.unlisten('channel2')\n\nEvent objects\n-------------\n\nThe event objects returned by pubsub.events() and pubsub.get_event() are\ninstances of psycopg2's `Notify class`_. They have three possibly-interesting\nattributes:\n\n- payload: A string containing the actual message.\n- channel: The name of the channel to which the event was sent.\n- pid: The pid of the process on the Postgres server that's handling the\n sender's connection. This can be useful to prevent an endless loop in a\n program that both sends and receives events::\n\n my_pid = pubsub.conn.get_backend_pid()\n pubsub.listen('echo')\n for e in pubsub.events():\n sender_pid = e.pid\n if sender_pid != my_pid:\n pubsub.notify('echo', e.payload)\n\nQ & A\n=====\n\n**Is it safe to pass pubsub objects between threads?**\n\nNo.\n\n**Why use the verbs 'notify' and 'listen' instead of 'publish' and\n'subscribe'?**\n\nThe methods in pgpubsub are designed to look as much as possible like the actual\nSQL commands in Postgres, which are NOTIFY_ and LISTEN_. The Postgres docs also\nrefer to 'notification events' rather than 'messages', so pgpubsub uses the same\nterm.\n\n**Why is there no callback-style interface?**\n\nSomeday there might be, if there's demand for it and a well-reasoned spec.\n\n.. _event notification system: http://www.postgresql.org/docs/9.4/static/sql-notify.html\n.. _PostgreSQL: http://www.postgresql.org/\n.. _Redis: http://redis.io/topics/pubsub\n.. _psycopg2.connect(): http://initd.org/psycopg/docs/module.html#psycopg2.connect\n.. _SQL identifiers: http://www.postgresql.org/docs/9.4/static/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS\n.. _Mandatory XKCD: https://xkcd.com/327/\n.. _select.select: https://docs.python.org/2/library/select.html#select.select\n.. _Notify class: http://initd.org/psycopg/docs/extensions.html?highlight=notify#psycopg2.extensions.Notify\n.. _NOTIFY: http://www.postgresql.org/docs/9.4/static/sql-notify.html\n.. _LISTEN: http://www.postgresql.org/docs/9.4/static/sql-listen.html", "description_content_type": null, "docs_url": null, "download_url": "UNKNOWN", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://bitbucket.org/btubbs/pgpubsub", "keywords": null, "license": "UNKNOWN", "maintainer": null, "maintainer_email": null, "name": "pgpubsub", "package_url": "https://pypi.org/project/pgpubsub/", "platform": "UNKNOWN", "project_url": "https://pypi.org/project/pgpubsub/", "project_urls": { "Download": "UNKNOWN", "Homepage": "https://bitbucket.org/btubbs/pgpubsub" }, "release_url": "https://pypi.org/project/pgpubsub/0.0.5/", "requires_dist": null, "requires_python": null, "summary": "A convenient Python interface to Postgres's LISTEN and NOTIFY features.", "version": "0.0.5" }, "last_serial": 1639687, "releases": { "0.0.1": [ { "comment_text": "", "digests": { "md5": "aec02f1cfece4ebd3206aded5021e22b", "sha256": "cca98a022dc1a967a86961e49160d768ca7d5645d045cdebf3d2342a52356a0e" }, "downloads": -1, "filename": "pgpubsub-0.0.1.tar.gz", "has_sig": false, "md5_digest": "aec02f1cfece4ebd3206aded5021e22b", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 1385, "upload_time": "2015-05-21T07:06:41", "url": "https://files.pythonhosted.org/packages/64/63/feddba42a1b381d83b678a7f516f47ce821715759f5d2d332e2776454a94/pgpubsub-0.0.1.tar.gz" } ], "0.0.2": [ { "comment_text": "", "digests": { "md5": "6b6d67cc9141cf8ddae08991b5976c4f", "sha256": "738b9c0ae28acd7fc0016117aa9828ab2b56e66758dde17cb3dbbd0f59c8adcc" }, "downloads": -1, "filename": "pgpubsub-0.0.2.tar.gz", "has_sig": false, "md5_digest": "6b6d67cc9141cf8ddae08991b5976c4f", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 3831, "upload_time": "2015-05-21T19:27:32", "url": "https://files.pythonhosted.org/packages/86/ac/3f9280c863c9a67c4644c4260e396efb67c0c2ae3938068fb1b4c0df99d0/pgpubsub-0.0.2.tar.gz" } ], "0.0.4": [ { "comment_text": "", "digests": { "md5": "50319482590a2f4e42fa384c44a35e82", "sha256": "e686f4af1914881a596ccb45b655f85abcdf45f6fe414821f0fd9cb642e7f56d" }, "downloads": -1, "filename": "pgpubsub-0.0.4.tar.gz", "has_sig": false, "md5_digest": "50319482590a2f4e42fa384c44a35e82", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 4358, "upload_time": "2015-05-21T20:30:22", "url": "https://files.pythonhosted.org/packages/e5/f6/406d1fa3c6cd538a258b4d261bce60e9ffd0098cda335b31570c4a244bb7/pgpubsub-0.0.4.tar.gz" } ], "0.0.5": [ { "comment_text": "", "digests": { "md5": "8a1e57fdcd50cd17fa970a921a057cc6", "sha256": "7f34ba700f2802537564a5329f20723772b99dfbf28d94856228b21e0af3c9a6" }, "downloads": -1, "filename": "pgpubsub-0.0.5.tar.gz", "has_sig": false, "md5_digest": "8a1e57fdcd50cd17fa970a921a057cc6", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 5247, "upload_time": "2015-07-19T00:00:36", "url": "https://files.pythonhosted.org/packages/ba/80/b36c9275c7197fa66733761a411f66d92086d524acbab08f56858f561fa8/pgpubsub-0.0.5.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "8a1e57fdcd50cd17fa970a921a057cc6", "sha256": "7f34ba700f2802537564a5329f20723772b99dfbf28d94856228b21e0af3c9a6" }, "downloads": -1, "filename": "pgpubsub-0.0.5.tar.gz", "has_sig": false, "md5_digest": "8a1e57fdcd50cd17fa970a921a057cc6", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 5247, "upload_time": "2015-07-19T00:00:36", "url": "https://files.pythonhosted.org/packages/ba/80/b36c9275c7197fa66733761a411f66d92086d524acbab08f56858f561fa8/pgpubsub-0.0.5.tar.gz" } ] }