{
"info": {
"author": "Jim Fulton",
"author_email": "jim@zope.com",
"bugtrack_url": null,
"classifiers": [],
"description": "=================================================\nService registration and discovery with ZooKeeper\n=================================================\n\nThe zc.zk package provides support for registering and discovering\nservices with ZooKeeper. It also provides support for defining\nservices with a tree-based model and syncing the model with ZooKeeper.\n\nThe use cases are:\n\n- Register a server providing a service.\n- Get the addresses of servers providing a service.\n- Get and set service configuration data.\n- Model system architecture as a tree.\n\nImportant note for zc.zk 1.x users\n Version 2 is mostly. but not entirely backward compatible.\n\n Although the goal of version 1 was primarily service registration\n and discovery, it also provided a high-level ZooKeeper API. `Kazoo\n `_ is a much better high-level\n interface for ZooKeeper because:\n\n - It isn't based on the buggy ZooKeeper C interface and Python\n extension.\n\n - It doesn't assume that ephemeral nodes should be reestablished\n when a session expires and is recreated.\n\n zc.zk 2 uses Kazoo.\n\nThis package makes no effort to support Windows. (Patches to support\nWindows might be accepted if they don't add much complexity.)\n\n.. contents::\n\nInstallation\n============\n\nYou can install this as you would any other distribution.\nIt requires the kazoo Python ZooKeeper interface.\n\nInstantiating a ZooKeeper helper\n================================\n\nTo use the helper API, create a ZooKeeper instance::\n\n >>> import zc.zk\n >>> zk = zc.zk.ZooKeeper('zookeeper.example.com:2181')\n\nThe ZooKeeper constructor takes a ZooKeeper connection string, which is\na comma-separated list of addresses of the form *HOST:PORT*. It\ndefaults to the value of the ``ZC_ZK_CONNECTION_STRING`` environment\nvariable, if set, or ``'127.0.0.1:2181'`` if not, which is convenient\nduring development.\n\nYou can also pass a kazoo client object, instead of a connection string.\n\n\nRegister a server providing a service\n=====================================\n\nTo register a server, use the ``register`` method, which takes\na service path and the address a server is listing on::\n\n >>> zk.register('/fooservice/providers', ('192.168.0.42', 8080))\n\n.. test\n\n >>> import os\n >>> zk.get_properties('/fooservice/providers/192.168.0.42:8080'\n ... ) == dict(pid=os.getpid())\n True\n\n\n``register`` creates a read-only ephemeral ZooKeeper node as a\nchild of the given service path. The name of the new node is (a\nstring representation of) the given address. This allows clients to\nget the list of addresses by just getting the list of the names of\nchildren of the service path.\n\nEphemeral nodes have the useful property that they're automatically\nremoved when a ZooKeeper session is closed or when the process\ncontaining it dies. De-registration is automatic.\n\nWhen registering a server, you can optionally provide server (node)\ndata as additional keyword arguments to register. By default,\nthe process id is set as the ``pid`` property. This is useful to\ntracking down the server process. In addition, an event is generated,\nproviding subscribers to add properties as a server is being\nregistered. (See `Server-registration events`_.)\n\nGet the addresses of service providers\n======================================\n\nGetting the addresses providing a service is accomplished by getting the\nchildren of a service node::\n\n >>> addresses = zk.children('/fooservice/providers')\n >>> sorted(addresses)\n ['192.168.0.42:8080']\n\nThe ``children`` method returns an iterable of names of child nodes of\nthe node specified by the given path. The iterable is automatically\nupdated when new providers are registered::\n\n >>> zk.register('/fooservice/providers', ('192.168.0.42', 8081))\n >>> sorted(addresses)\n ['192.168.0.42:8080', '192.168.0.42:8081']\n\nYou can also get the number of children with ``len``::\n\n >>> len(addresses)\n 2\n\nYou can call the iterable with a callback function that is called\nwhenever the list of children changes::\n\n >>> @zk.children('/fooservice/providers')\n ... def addresses_updated(addresses):\n ... print 'addresses changed'\n ... print sorted(addresses)\n addresses changed\n ['192.168.0.42:8080', '192.168.0.42:8081']\n\nThe callback is called immediately with the children. When we add\nanother child, it'll be called again::\n\n >>> zk.register('/fooservice/providers', ('192.168.0.42', 8082))\n addresses changed\n ['192.168.0.42:8080', '192.168.0.42:8081', '192.168.0.42:8082']\n\nGet service configuration data\n==============================\n\nYou get service configuration data by getting properties associated with a\nZooKeeper node. The interface for getting properties is similar to the\ninterface for getting children::\n\n >>> data = zk.properties('/fooservice')\n >>> data['database']\n u'/databases/foomain'\n >>> data['threads']\n 1\n\nThe ``properties`` method returns a mapping object that provides access to\nnode data. (ZooKeeper only stores string data for nodes. ``zc.zk``\nprovides a higher-level data interface by storing JSON strings.)\n\nThe properties objects can be called with callback functions and used\nas function decorators to get update notification::\n\n >>> @zk.properties('/fooservice')\n ... def data_updated(data):\n ... print 'data updated'\n ... for item in sorted(data.items()):\n ... print '%s: %r' % item\n data updated\n database: u'/databases/foomain'\n favorite_color: u'red'\n threads: 1\n\nThe callback is called immediately. It'll also be called when data are\nupdated.\n\nUpdating node properties\n========================\n\nYou can update properties by calling the ``update`` method::\n\n >>> thread_info = {'threads': 2}\n >>> data.update(thread_info, secret='123')\n data updated\n database: u'/databases/foomain'\n favorite_color: u'red'\n secret: u'123'\n threads: 2\n\nYou can also set individual properties:\n\n >>> data['threads'] = 1\n data updated\n database: u'/databases/foomain'\n favorite_color: u'red'\n secret: u'123'\n threads: 1\n\nIf you call the ``set`` method, keys not listed are removed:\n\n >>> data.set(threads= 3, secret='1234')\n data updated\n secret: u'1234'\n threads: 3\n\nBoth ``update`` and ``set`` can take data from a positional data argument, or\nfrom keyword parameters. Keyword parameters take precedent over the\npositional data argument.\n\nGetting property data without tracking changes\n==============================================\n\n\nSometimes, you want to get service data, but don't want to watch for\nchanges. If you pass ``watch=False`` to ``properties``, Then properties\nwon't track changes. In this case, you can't set callback functions,\nbut you can still update data.\n\n.. test\n\n >>> p2 = zk.properties('/fooservice', watch=False)\n >>> sorted(p2)\n [u'secret', u'threads']\n >>> p2(lambda data: None)\n Traceback (most recent call last):\n ...\n TypeError: Can't set callbacks without watching.\n\n >>> p2['threads'] = 2 # doctest: +ELLIPSIS\n data updated\n ...\n threads: 2\n >>> p2.update(threads=3) # doctest: +ELLIPSIS\n data updated\n ...\n threads: 3\n\nTree-definition format, import, and export\n==========================================\n\nYou can describe a ZooKeeper tree using a textual tree\nrepresentation. You can then populate the tree by importing the\nrepresentation. Heres an example::\n\n /lb : ipvs\n /pools\n /cms\n # The address is fixed because it's\n # exposed externally\n address = '1.2.3.4:80'\n providers -> /cms/providers\n /retail\n address = '1.2.3.5:80'\n providers -> /cms/providers\n\n /cms : z4m cms\n threads = 3\n /providers\n /databases\n /main\n /providers\n\n /retail : z4m retail\n threads = 1\n /providers\n /databases\n main -> /cms/databases/main\n /ugc\n /providers\n\n.. -> tree_text\n\nThis example defines a tree with 3 top nodes, ``lb`` and ``cms``, and\n``retail``. The ``retail`` node has two sub-nodes, ``providers`` and\n``databases`` and a property ``threads``.\n\nThe ``/retail/databases`` node has symbolic link, ``main`` and a\n``ugc`` sub-node. The symbolic link is implemented as a property named\n`` We'll say more about symbolic links in a later section.\n\nThe ``lb``, ``cms`` and ``retail`` nodes have *types*. A type is\nindicated by following a node name with a colon and a string value.\nThe string value is used to populate a ``type`` property. Types are\nuseful to document the kinds of services provided at a node and can be\nused by deployment tools to deploy service providers.\n\nYou can import a tree definition with the ``import_tree`` method::\n\n >>> zk.import_tree(tree_text)\n\nThis imports the tree at the top of the ZooKeeper tree.\n\nWe can also export a ZooKeeper tree::\n\n >>> print zk.export_tree(),\n /cms : z4m cms\n threads = 3\n /databases\n /main\n /providers\n /providers\n /fooservice\n secret = u'1234'\n threads = 3\n /providers\n /lb : ipvs\n /pools\n /cms\n address = u'1.2.3.4:80'\n providers -> /cms/providers\n /retail\n address = u'1.2.3.5:80'\n providers -> /cms/providers\n /retail : z4m retail\n threads = 1\n /databases\n main -> /cms/databases/main\n /ugc\n /providers\n /providers\n\nNote that when we export a tree:\n\n- The special reserved top-level zookeeper node is omitted.\n- Ephemeral nodes are omitted.\n- Each node's information is sorted by type (properties, then links,\n- then sub-nodes) and then by name,\n\nYou can export just a portion of a tree::\n\n >>> print zk.export_tree('/fooservice'),\n /fooservice\n secret = u'1234'\n threads = 3\n /providers\n\nYou can optionally see ephemeral nodes::\n\n >>> print zk.export_tree('/fooservice', ephemeral=True),\n /fooservice\n secret = u'1234'\n threads = 3\n /providers\n /192.168.0.42:8080\n pid = 9999\n /192.168.0.42:8081\n pid = 9999\n /192.168.0.42:8082\n pid = 9999\n\nWe can import a tree over an existing tree and changes will be\napplied. Let's update our textual description::\n\n /lb : ipvs\n /pools\n /cms\n # The address is fixed because it's\n # exposed externally\n address = '1.2.3.4:80'\n providers -> /cms/providers\n\n /cms : z4m cms\n threads = 4\n /providers\n /databases\n /main\n /providers\n\n.. -> tree_text\n\nand re-import::\n\n >>> zk.import_tree(tree_text)\n extra path not trimmed: /lb/pools/retail\n\nWe got a warning about nodes left over from the old tree. We can see\nthis if we look at the tree::\n\n >>> print zk.export_tree(),\n /cms : z4m cms\n threads = 4\n /databases\n /main\n /providers\n /providers\n /fooservice\n secret = u'1234'\n threads = 3\n /providers\n /lb : ipvs\n /pools\n /cms\n address = u'1.2.3.4:80'\n providers -> /cms/providers\n /retail\n address = u'1.2.3.5:80'\n providers -> /cms/providers\n /retail : z4m retail\n threads = 1\n /databases\n main -> /cms/databases/main\n /ugc\n /providers\n /providers\n\nIf we want to trim these, we can add a ``trim`` option. This is a\nlittle scary, so we'll use the dry-run option to see what it's going\nto do::\n\n >>> zk.import_tree(tree_text, trim=True, dry_run=True)\n would delete /lb/pools/retail.\n\nIf we know we're not trimming things and want to avoid a warning, we\ncan use trim=False:\n\n >>> zk.import_tree(tree_text, trim=False)\n\nWe can see that this didn't trim by using dry-run again:\n\n >>> zk.import_tree(tree_text, trim=True, dry_run=True)\n would delete /lb/pools/retail.\n\nWe do want to trim, so we use trim=True:\n\n >>> zk.import_tree(tree_text, trim=True)\n >>> print zk.export_tree(),\n /cms : z4m cms\n threads = 4\n /databases\n /main\n /providers\n /providers\n /fooservice\n secret = u'1234'\n threads = 3\n /providers\n /lb : ipvs\n /pools\n /cms\n address = u'1.2.3.4:80'\n providers -> /cms/providers\n /retail : z4m retail\n threads = 1\n /databases\n main -> /cms/databases/main\n /ugc\n /providers\n /providers\n\nNote that nodes containing (directly or recursively) ephemeral nodes\nwill never be trimmed. Also node that top-level nodes are never\nautomatically trimmed. So we weren't warned about the unreferenced\ntop-level nodes in the import.\n\nRecursive deletion\n==================\n\nZooKeeper only allows deletion of nodes without children.\nThe ``delete_recursive`` method automates removing a node and all of\nit's children.\n\nIf we want to remove the ``retail`` top-level node, we can use\ndelete_recursive::\n\n >>> zk.delete_recursive('/retail')\n >>> print zk.export_tree(),\n /cms : z4m cms\n threads = 4\n /databases\n /main\n /providers\n /providers\n /fooservice\n secret = u'1234'\n threads = 3\n /providers\n /lb : ipvs\n /pools\n /cms\n address = u'1.2.3.4:80'\n providers -> /cms/providers\n\n\nBt default, ``delete_recursive`` won't delete ephemeral nodes, or\nnodes that contain them::\n\n >>> zk.delete_recursive('/fooservice')\n Not deleting /fooservice/providers/192.168.0.42:8080 because it's ephemeral.\n Not deleting /fooservice/providers/192.168.0.42:8081 because it's ephemeral.\n Not deleting /fooservice/providers/192.168.0.42:8082 because it's ephemeral.\n /fooservice/providers not deleted due to ephemeral descendent.\n /fooservice not deleted due to ephemeral descendent.\n\nYou can use the ``force`` option to force ephemeral nodes to be\ndeleted.\n\nSymbolic links\n==============\n\nZooKeeper doesn't have a concept of symbolic links, but ``zc.zk``\nprovides a convention for dealing with symbolic links. When trying to\nresolve a path, if a node lacks a child, but has a property with a\nname ending in ``' ->'``, the child will be found by following the\npath in the property value.\n\nThe ``resolve`` method is used to resolve a path to a real path::\n\n >>> zk.resolve('/lb/pools/cms/providers')\n u'/cms/providers'\n\nIn this example, the link was at the endpoint of the virtual path, but\nit could be anywhere::\n\n >>> zk.register('/cms/providers', '1.2.3.4:5')\n >>> zk.resolve('/lb/pools/cms/providers/1.2.3.4:5')\n u'/cms/providers/1.2.3.4:5'\n\nNote a limitation of symbolic links is that they can be hidden by\nchildren. For example, if we added a real node, at\n``/lb/pools/cms/provioders``, it would shadow the link.\n\n``children``, ``properties``, and ``register`` will\nautomatically use ``resolve`` to resolve paths.\n\nWhen the ``children`` and ``properties`` are used for a node, the\npaths they use will be adjusted dynamically when paths are removed.\nTo illustrate this, let's get children of ``/cms/databases/main``::\n\n >>> main_children = zk.children('/cms/databases/main')\n >>> main_children.path\n '/cms/databases/main'\n >>> main_children.real_path\n '/cms/databases/main'\n\n.. test\n\n >>> main_properties = zk.properties('/cms/databases/main')\n >>> main_properties.path\n '/cms/databases/main'\n >>> main_properties.real_path\n '/cms/databases/main'\n\n``Children`` and ``Properties`` objects have a ``path`` attribute that\nhas the value passed to the ``children`` or ``properties``\nmethods. They have a ``real_path`` attribute that contains the path\nafter resolving symbolic links. Let's suppose we want to move the\ndatabase node to '/databases/cms'. First we'll export it::\n\n >>> export = zk.export_tree('/cms/databases/main', name='cms')\n >>> print export,\n /cms\n /providers\n\nNote that we used the export ``name`` option to specify a new name for\nthe exported tree.\n\nNow, we'll create a databases node::\n\n >>> zk.create('/databases')\n u'/databases'\n\nAnd import the export::\n\n >>> zk.import_tree(export, '/databases')\n >>> print zk.export_tree('/databases'),\n /databases\n /cms\n /providers\n\nNext, we'll create a symbolic link at the old location. We can use the\n``ln`` convenience method::\n\n >>> zk.ln('/databases/cms', '/cms/databases/main')\n >>> zk.get_properties('/cms/databases')\n {u'main ->': u'/databases/cms'}\n\nNow, we can remove ``/cms/databases/main`` and ``main_children`` will\nbe updated::\n\n >>> zk.delete_recursive('/cms/databases/main')\n >>> main_children.path\n '/cms/databases/main'\n >>> main_children.real_path\n u'/databases/cms'\n\n.. test\n\n >>> main_properties.path\n '/cms/databases/main'\n >>> main_properties.real_path\n u'/databases/cms'\n\nIf we update ``/databases/cms``, ``main_children`` will see the\nupdates::\n\n >>> sorted(main_children)\n ['providers']\n >>> _ = zk.delete('/databases/cms/providers')\n >>> sorted(main_children)\n []\n\n.. test\n\n >>> dict(main_properties)\n {}\n >>> zk.properties('/databases/cms').set(a=1)\n >>> dict(main_properties)\n {u'a': 1}\n\nSymbolic links can be relative. If a link doesn't start with a slash,\nit's interpreted relative to the node the link occurs in. The special\nnames ``.`` and ``..`` have their usual meanings.\n\nSo, in::\n\n /a\n /b\n l -> c\n l2 -> ../c\n /c\n /c\n\n.. -> relative_link_source\n\n >>> zk.import_tree(relative_link_source)\n >>> zk.resolve('/a/b/l')\n u'/a/b/c'\n >>> zk.resolve('/a/b/l2')\n u'/a/c'\n\n >>> zk.delete_recursive('/a')\n\nThe link at ``/a/b/l`` resolves to ``/a/b/c`` and ``/a/b/l2`` resolves\nto ``/a/c``.\n\nProperty links\n==============\n\nIn addition to symbolic links between nodes, you can have links\nbetween properties. In our earlier example, both the ``/cms`` and\n``/fooservice`` nodes had ``threads`` properties::\n\n /cms : z4m cms\n threads = 4\n /databases\n /main\n /providers\n /providers\n /fooservice\n secret = u'1234'\n threads = 3\n /providers\n /lb : ipvs\n /pools\n /cms\n address = u'1.2.3.4:80'\n providers -> /cms/providers\n\nIf we wanted ``/cms`` to have the same ``threads`` settings, we could\nuse a property link::\n\n /cms : z4m cms\n threads => /fooservice threads\n /databases\n /main\n /providers\n /providers\n /fooservice\n secret = u'1234'\n threads = 3\n /providers\n /lb : ipvs\n /pools\n /cms\n address = u'1.2.3.4:80'\n providers -> /cms/providers\n\n.. -> property_link_source\n\n >>> _ = zk.create('/test-propery-links', '', zc.zk.OPEN_ACL_UNSAFE)\n\n >>> zk.import_tree(property_link_source, '/test-propery-links')\n >>> properties = zk.properties('/test-propery-links/cms')\n >>> properties['threads =>']\n u'/fooservice threads'\n >>> properties['threads']\n 3\n\n >>> zk.import_tree('/cms\\n threads => /fooservice\\n',\n ... '/test-propery-links')\n extra path not trimmed: /test-propery-links/cms/databases\n extra path not trimmed: /test-propery-links/cms/providers\n >>> properties['threads =>']\n u'/fooservice'\n >>> properties['threads']\n 3\n\n >>> zk.delete_recursive('/test-propery-links')\n\nProperty links are indicated with ``=>``. The value is a node path and\noptional property name, separated by whitespace. If the name is\nommitted, then the refering name is used. For example, the name could\nbe left off of the property link above.\n\nNode deletion\n=============\n\nIf a node is deleted and ``Children`` or ``Properties`` instances have\nbeen created for it, and the paths they were created with can't be\nresolved using symbolic links, then the instances' data will be\ncleared. Attempts to update properties will fail. If callbacks have\nbeen registered, they will be called without arguments, if possible.\nIt would be bad, in practice, to remove a node that processes are\nwatching.\n\nRegistering a server with a blank hostname\n==========================================\n\nIt's common to use an empty string for a host name when calling bind\nto listen on all IPv4 interfaces. If you pass an address with an\nempty host to ``register`` and `netifaces\n`_ is installed, then\nall of the non-local IPv4 addresses [#ifaces]_ (for the given port) will be\nregistered.\n\nIf there are no non-local interfaces (not connected to network), then\nthe local IPV4 interface will be registered.\n\nIf netifaces isn't installed and you pass an empty host\nname, then the fully-qualified domain name, as returned by\n``socket.getfqdn()`` will be used for the host.\n\nServer-registration events\n==========================\n\nWhen ``register`` is called, a ``zc.zk.RegisteringServer``\nevent is emmitted with a properties attribute that can be updated by\nsubscribers prior to creating the ZooKeeper ephemeral node. This\nallows third-party code to record extra server information.\n\nEvents are emitted by passing them to ``zc.zk.event.notify``. If\n``zope.event`` is installed, then ``zc.zk.event.notify`` is an alias\nfor ``zope.event.notify``, otherwise, ``zc.zk.event.notify`` is an\nempty function that can be replaced by applications.\n\nZooKeeper Session Management\n============================\n\nKazoo takes care of reestablishing ZooKeeper sessions. Watches created\nwith the ``children`` and ``properties`` methods are reestablished when\nnew sessions are established. ``zc.zk`` also recreates ephemeral\nnodes created via ``register``.\n\nzookeeper_export script\n=======================\n\nThe `zc.zk` package provides a utility script for exporting a\nZooKeeper tree::\n\n $ zookeeper_export -e zookeeper.example.com:2181 /fooservice\n /fooservice\n secret = u'1234'\n threads = 3\n /providers\n /192.168.0.42:8080\n pid = 9999\n /192.168.0.42:8081\n pid = 9999\n /192.168.0.42:8082\n pid = 9999\n\n.. -> sh\n\n >>> command, expected = sh.strip().split('\\n', 1)\n >>> _, command, args = command.split(None, 2)\n >>> import pkg_resources\n >>> export = pkg_resources.load_entry_point(\n ... 'zc.zk', 'console_scripts', command)\n >>> import sys, StringIO\n >>> sys.stdout = f = StringIO.StringIO(); export(args.split())\n >>> got = f.getvalue()\n >>> import zc.zk.tests\n >>> zc.zk.tests.checker.check_output(expected.strip(), got.strip(), 0)\n True\n\n >>> export(['zookeeper.example.com:2181', '/fooservice'])\n /fooservice\n secret = u'1234'\n threads = 3\n /providers\n\n >>> export(['zookeeper.example.com:2181'])\n /cms : z4m cms\n threads = 4\n /databases\n main -> /databases/cms\n /providers\n /databases\n /cms\n a = 1\n /fooservice\n secret = u'1234'\n threads = 3\n /providers\n /lb : ipvs\n /pools\n /cms\n address = u'1.2.3.4:80'\n providers -> /cms/providers\n\n >>> export(['zookeeper.example.com:2181', '/fooservice', '-oo'])\n >>> print open('o').read(),\n /fooservice\n secret = u'1234'\n threads = 3\n /providers\n\nThe export script provides the same features as the ``export_tree``\nmethod. Use the ``--help`` option to see how to use it.\n\nzookeeper_import script\n=======================\n\nThe `zc.zk` package provides a utility script for importing a\nZooKeeper tree. So, for example, given the tree::\n\n /provision\n /node1\n /node2\n\n.. -> file_source\n\n >>> with open('mytree.txt', 'w') as f: f.write(file_source)\n\nIn the file ``mytree.txt``, we can import the file like this::\n\n $ zookeeper_import zookeeper.example.com:2181 mytree.txt /fooservice\n\n.. -> sh\n\n >>> command = sh.strip()\n >>> expected = ''\n >>> _, command, args = command.split(None, 2)\n >>> import_ = pkg_resources.load_entry_point(\n ... 'zc.zk', 'console_scripts', command)\n >>> import_(args.split())\n\n >>> zk.print_tree()\n /cms : z4m cms\n threads = 4\n /databases\n main -> /databases/cms\n /providers\n /1.2.3.4:5\n pid = 9999\n /databases\n /cms\n a = 1\n /fooservice\n secret = u'1234'\n threads = 3\n /providers\n /192.168.0.42:8080\n pid = 9999\n /192.168.0.42:8081\n pid = 9999\n /192.168.0.42:8082\n pid = 9999\n /provision\n /node1\n /node2\n /lb : ipvs\n /pools\n /cms\n address = u'1.2.3.4:80'\n providers -> /cms/providers\n\n Read from stdin:\n\n >>> stdin = sys.stdin\n >>> sys.stdin = StringIO.StringIO('/x\\n/y')\n >>> import_('-d zookeeper.example.com:2181 - /fooservice'.split())\n add /fooservice/x\n add /fooservice/y\n\n >>> sys.stdin = StringIO.StringIO('/x\\n/y')\n >>> import_('-d zookeeper.example.com:2181'.split())\n add /x\n add /y\n\n Trim:\n\n >>> sys.stdin = StringIO.StringIO('/provision\\n/y')\n >>> import_('-dt zookeeper.example.com:2181 - /fooservice'.split())\n would delete /fooservice/provision/node1.\n would delete /fooservice/provision/node2.\n add /fooservice/y\n\n >>> sys.stdin = stdin\n\nThe import script provides the same features as the ``import_tree``\nmethod, with the exception that it provides less flexibility for\nspecifing access control lists. Use the ``--help`` option to see how\nto use it.\n\nPropery-update script\n=====================\n\nThe `zc.zk` package provides a utility script for updating individual\nproperties::\n\n zookeeper_set_property zookeeper.example.com:2181 /fooservice \\\n threads=4 debug=True comment='ok'\n\n.. -> example\n\n >>> example = example.replace('\\\\', '')\n >>> args = example.strip().split()\n >>> set_property = pkg_resources.load_entry_point(\n ... 'zc.zk', 'console_scripts', args.pop(0))\n >>> set_property(args)\n data updated\n comment: u'ok'\n debug: True\n secret: u'1234'\n threads: 4\n >>> zk.print_tree('/fooservice')\n /fooservice\n comment = u'ok'\n debug = True\n secret = u'1234'\n threads = 4\n /providers\n /192.168.0.42:8080\n pid = 9999\n /192.168.0.42:8081\n pid = 9999\n /192.168.0.42:8082\n pid = 9999\n /provision\n /node1\n /node2\n\n\nThe first argument to the script is the path of the node to be\nupdated. Any number of additional arguments of the form:\n``NAME=PYTHONEXPRESSION`` are provided to supply updates. If setting\nstrings, you may have to quote the argument, as in \"comment='a\ncomment'\".\n\nIterating over a tree\n=====================\n\nThe ``walk`` method can be used to walk over the nodes in a tree::\n\n >>> for path in zk.walk():\n ... print path\n /\n /cms\n /cms/databases\n /cms/providers\n /cms/providers/1.2.3.4:5\n /databases\n /databases/cms\n /fooservice\n /fooservice/providers\n /fooservice/providers/192.168.0.42:8080\n /fooservice/providers/192.168.0.42:8081\n /fooservice/providers/192.168.0.42:8082\n /fooservice/provision\n /fooservice/provision/node1\n /fooservice/provision/node2\n /lb\n /lb/pools\n /lb/pools/cms\n /zookeeper\n /zookeeper/quota\n\n >>> for path in zk.walk('/fooservice'):\n ... print path\n /fooservice\n /fooservice/providers\n /fooservice/providers/192.168.0.42:8080\n /fooservice/providers/192.168.0.42:8081\n /fooservice/providers/192.168.0.42:8082\n /fooservice/provision\n /fooservice/provision/node1\n /fooservice/provision/node2\n\nYou can omit ephemeral nodes:\n\n >>> for path in zk.walk('/fooservice', ephemeral=False):\n ... print path\n /fooservice\n /fooservice/providers\n /fooservice/providers/192.168.0.42:8080\n /fooservice/providers/192.168.0.42:8081\n /fooservice/providers/192.168.0.42:8082\n /fooservice/provision\n /fooservice/provision/node1\n /fooservice/provision/node2\n\nYou can also get a mutable list of children, which you can mutate:\n\n >>> i = zk.walk('/fooservice', children=True)\n >>> path, children = i.next()\n >>> path, children\n ('/fooservice', [u'providers', u'provision'])\n\n >>> del children[0]\n >>> for path in i:\n ... print path\n /fooservice/provision\n /fooservice/provision/node1\n /fooservice/provision/node2\n\n\nModifications to nodes are reflected while traversing::\n\n >>> for path in zk.walk('/fooservice'):\n ... print path\n ... if 'provision' in zk.get_children(path):\n ... zk.delete_recursive(path+'/provision')\n /fooservice\n /fooservice/providers\n /fooservice/providers/192.168.0.42:8080\n /fooservice/providers/192.168.0.42:8081\n /fooservice/providers/192.168.0.42:8082\n\n\nGraph analysis\n==============\n\nThe textual tree representation can be used to model and analyze a\nsystem architecturte. You can get a parsed representation of a tree\nusing ``zc.zk.parse_tree`` to parse a text tree representation\ngenerated by hand for import, or using the ``export_tree`` method.\n\n::\n\n >>> tree = zc.zk.parse_tree(tree_text)\n >>> sorted(tree.children)\n ['cms', 'lb']\n >>> tree.children['lb'].properties\n {'type': 'ipvs'}\n\nThe demo module, ``zc.zk.graphvis`` shows how you might generate\nsystem diagrams from tree models.\n\nReference\n=========\n\nzc.zk.ZooKeeper\n---------------\n\n``zc.zk.ZooKeeper([connection_string[, session_timeout[, wait]]])``\n Return a new instance given a ZooKeeper connection string.\n\n The connection string defaults to the value of the\n ``ZC_ZK_CONNECTION_STRING`` environment variable, if set, otherwise\n '127.0.0.1:2181' will be used.\n\n If a session timeout (``session_timeout``) isn't specified, the\n ZooKeeper server's default session timeout is used. If the\n connection to ZooKeeper flaps, setting this to a higher value can\n avoid having clients think a server has gone away, when it hasn't.\n The downside of setting this to a higher value is that if a server\n crashes, it will take longer for ZooKeeper to notice that it's\n gone.\n\n The ``wait`` flag indicates whether the constructor should wait\n for a connection to ZooKeeper. It defaults to False.\n\n If a connection can't be made, a ``zc.zk.FailedConnect`` exception\n is raised.\n\n``children(path)``\n Return a `zc.zk.Children`_ for the path.\n\n Note that there is a fair bit of machinery in `zc.zk.Children`_\n objects to support keeping them up to date, callbacks, and cleaning\n them up when they are no-longer used. If you only want to get the\n list of children once, use ``get_children``.\n\n``close()``\n Close the ZooKeeper session.\n\n This should be called when cleanly shutting down servers to more\n quickly remove ephemeral nodes.\n\n``delete_recursive(path[, dry_run[, force[, ignore_if_ephemeral]]])``\n Delete a node and all of it's sub-nodes.\n\n Ephemeral nodes or nodes containing them are not deleted by\n default. To force deletion of ephemeral nodes, supply the ``force``\n option with a true value.\n\n Normally, a message is printed if a node can't be deleted because\n it's ephemeral or has ephemeral sub-nodes. If the\n ``ignore_if_ephemeral`` option is true, the a message isn't printed\n if the node's path was passed to ``delete_recursive`` directly.\n (This is used by ``import_tree`` when the only nodes that would be\n trimmed are ephemeral nodes.)\n\n The dry_run option causes a summary of what would be deleted to be\n printed without actually deleting anything.\n\n``export_tree(path[, ephemeral[, name]])``\n Export a tree to a text representation.\n\n path\n The path to export.\n\n ephemeral\n Boolean, defaulting to false, indicating whether to include\n ephemeral nodes in the export. Including ephemeral nodes is\n mainly useful for visualizing the tree state.\n\n name\n The name to use for the top-level node.\n\n This is useful when using export and import to copy a tree to\n a different location and name in the hierarchy.\n\n Normally, when exporting the root node, ``/``, the root isn't\n included, but it is included if a name is given.\n\n``import_tree(text[, path='/'[, trim[, acl[, dry_run]]]])``\n Create tree nodes by importing a textual tree representation.\n\n text\n A textual representation of the tree.\n\n path\n The path at which to create the top-level nodes.\n\n trim\n Boolean, defaulting to false, indicating whether nodes not in\n the textual representation should be removed.\n\n acl\n An access control-list to use for imported nodes. If not\n specified, then full access is allowed to everyone.\n\n dry_run\n Boolean, defaulting to false, indicating whether to do a dry\n run of the import, without applying any changes.\n\n``is_ephemeral(path)``\n Return ``True`` if the node at ``path`` is ephemeral,``False`` otherwise.\n\n``ln(source, destination)``\n Create a symbolic link at the destination path pointing to the\n source path.\n\n If the destination path ends with ``'/'``, then the source name is\n appended to the destination.\n\n``print_tree(path='/')``\n Print the tree at the given path.\n\n This is just a short-hand for::\n\n print zk.export_tree(path, ephemeral=True),\n\n``properties(path, watch=True)``\n Return a `zc.zk.Properties`_ for the path.\n\n Note that there is a fair bit of machinery in `zc.zk.Properties`_\n objects to support keeping them up to date, callbacks, and cleaning\n them up when they are no-longer used. If you don't want to track\n changes, pass ``watch=False``.\n\n``register(path, address, acl=zc.zk.READ_ACL_UNSAFE, **data)``\n Register a server at a path with the address.\n\n An ephemeral child node of ``path`` will be created with name equal\n to the string representation (HOST:PORT) of the given address.\n\n ``address`` must be a host and port tuple.\n\n ``acl`` is a ZooKeeper access control list.\n\n Optional node properties can be provided as keyword arguments.\n\n``resolve(path)``\n Find the real path for the given path.\n\n``walk(path)``\n Iterate over the nodes of a tree rooted at path.\n\nIn addition, ``ZooKeeper`` instances provide shortcuts to the following\nkazoo client methods: ``exists``, ``create``, ``delete``,\n``get_children``, ``get``, and ``set``.\n\nzc.zk.Children\n--------------\n\n``__iter__()``\n Return an iterator over the child names.\n\n``__call__(callable)``\n Register a callback to be called whenever a child node is added or\n removed.\n\n The callback is passed the children instance when a child node is\n added or removed.\n\n The ``Children`` instance is returned.\n\nzc.zk.Properties\n----------------\n\nProperties objects provide the usual read-only mapping methods,\n__getitem__, __len__, etc..\n\n``set(data=None, **properties)``\n Set the properties for the node, replacing existing data.\n\n The data argument, if given, must be a dictionary or something that\n can be passed to the ``dict`` constructor. Items supplied as\n keywords take precedence over items supplied in the data argument.\n\n``update(data=None, **properties)``\n Update the properties for the node.\n\n The data argument, if given, must be a dictionary or something that\n can be passed to a dictionary's ``update`` method. Items supplied\n as keywords take precedence over items supplied in the data\n argument.\n\n``__call__(callable)``\n Register a callback to be called whenever a node's properties are changed.\n\n The callback is passed the properties instance when properties are\n changed.\n\n The ``Properties`` instance is returned.\n\nOther module attributes\n------------------------\n\n``zc.zk.ZK``\n A convenient aliad for ``zc.zk.ZooKeeper`` for people who hate to\n type.\n\nTesting support\n---------------\n\nThe ``zc.zk.testing`` module provides ``setUp`` and ``tearDown``\nfunctions that can be used to emulate a ZooKeeper server. To find out\nmore, use the help function::\n\n >>> import zc.zk.testing\n >>> help(zc.zk.testing)\n\n.. -> ignore\n\n >>> import zc.zk.testing\n\n.. cleanup\n\n >>> zk.close()\n\n\nChange History\n==============\n\n2.1.0 (2014-10-20)\n==================\n\n- Get the default connection string from ``ZC_ZK_CONNECTION_STRING`` if set.\n\n2.0.1 (2014-08-28)\n==================\n\n- Fixed: ZooKeeper operations (including closing ZooKeeper\n connections) hung after network failures if ZooKeeper sessions were\n lost and ephemeral nodes (for registered services) had to be\n re-registered.\n\n- Fixed: Didn't properly handle None values for node data returned by\n Kazoo 2.0.\n\n2.0.0 (2014-06-02)\n==================\n\nFinal release (identical to 2.0.0a7). We've used this in production\nfor several months.\n\n2.0.0a7 (2014-02-12)\n--------------------\n\nFixed: The release missed a zcml file helpful for registering\nmonitoring components.\n\n2.0.0a6 (2014-02-10)\n--------------------\n\nThis release has a number of backward-compatibility changes made after\ntesting some existing client software with the a5 release.\n\n- Restored the ``wait`` constructor flag to keep trying if a connection\n fails.\n\n- Restored the ``recv_timeout`` for test backward compatibility.\n\n- Restored the test handle-management mechanism for test\n backward-compatibility.\n\n- Fixed a bug in the way test machinery used internal handles.\n\n- Restored the create_recursive method for backward compatibility.\n\n2.0.0a5 (2014-01-30)\n--------------------\n\n- Log when sessions are lost and ephemeral nodes are restored.\n\n\nFixed: Kazoo returns node children as Unicode.\n zc.zk client applications expect children as\n returned by the children to have bytes values and\n they use the values to connect sockets.\n\n ``Children`` objects returned by zc.zk.children now encode\n child names using UTF-8.\n\nFixed: zc.zk 2 didn't accept a value of None for session_timeout\n constructor argument, breaking some old clients.\n\n2.0.0a4 (2014-01-13)\n--------------------\n\nFixed: When saving properties in ZooKeeper nodes, empty properties\n were encoded as empty strings. When Kazoo saves empty strings,\n it does so in a way that causes the ZooKeeper C client (or at\n least the Python C binding) to see semi-random data, sometimes\n including data written previously to other nodes. This can\n cause havoc when data for one node leaks into another.\n\n Now, we save empty properties as ``'{}'``.\n\n2.0.0a3 (2014-01-08)\n--------------------\n\n- Renamed ``get_raw_properties`` back to ``get_properties``, for\n backward compatibility, now that we've decided not to have a\n separate package.\n\n- Added ``ensure_path`` to the testing client.\n\n- Updated the ``ZooKeeper.close`` method to allow multiple calls.\n (Calls after the first have no effect.)\n\n2.0.0a2 (2014-01-06)\n--------------------\n\nFixed packaging bug.\n\n2.0.0a1 (2014-01-06)\n--------------------\n\nInitial version forked from zc.zk 1.2.0\n\n----------------------------------------------------------------------\n\n.. [#ifaces] It's a little more complicated. If there are non-local\n interfaces, then only non-local addresses are registered. In\n normal production, there's really no point in registering local\n addresses, as clients on other machines can't make any sense of\n them. If *only* local interfaces are found, then local addresses\n are registered, under the assumption that someone is developing on\n a disconnected computer.",
"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": "zc.zk",
"package_url": "https://pypi.org/project/zc.zk/",
"platform": "UNKNOWN",
"project_url": "https://pypi.org/project/zc.zk/",
"project_urls": {
"Download": "UNKNOWN",
"Homepage": "UNKNOWN"
},
"release_url": "https://pypi.org/project/zc.zk/2.1.0/",
"requires_dist": null,
"requires_python": null,
"summary": "Service registration and discovery with ZooKeeper",
"version": "2.1.0"
},
"last_serial": 1276705,
"releases": {
"0": [
{
"comment_text": "",
"digests": {
"md5": "8a1d316ba32157945afcc6c7d9db0f4c",
"sha256": "cc025c94e7d09790793d993c23edc936d741f617734d02077cdb504e8949e1e2"
},
"downloads": -1,
"filename": "zc.zk-0.tar.gz",
"has_sig": false,
"md5_digest": "8a1d316ba32157945afcc6c7d9db0f4c",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 63207,
"upload_time": "2012-09-25T19:56:56",
"url": "https://files.pythonhosted.org/packages/3c/e2/b18562b6a7eedcac98848563acb19b20f28610d475e22ef4bfaeee0085c6/zc.zk-0.tar.gz"
}
],
"0.1.0": [
{
"comment_text": "",
"digests": {
"md5": "19cd8ab48441e0d7544684b0c6a5bf78",
"sha256": "2bee8e93ca1c38f2481b49009efc294f46380904b15fd6180c3ef127c2dd7898"
},
"downloads": -1,
"filename": "zc.zk-0.1.0.tar.gz",
"has_sig": false,
"md5_digest": "19cd8ab48441e0d7544684b0c6a5bf78",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 17493,
"upload_time": "2011-11-27T23:25:27",
"url": "https://files.pythonhosted.org/packages/80/ff/63c2d8b8225b52c50eb9debf3e6d34dbbd86be89ee8e281d7d59db17f49f/zc.zk-0.1.0.tar.gz"
}
],
"0.2.0": [
{
"comment_text": "",
"digests": {
"md5": "89e8d4c299450eb1752876867c698da5",
"sha256": "77e36ebfd59c73df314ab687ab7deda49bf38d18151a1ac4a27412262a2967fb"
},
"downloads": -1,
"filename": "zc.zk-0.2.0.tar.gz",
"has_sig": false,
"md5_digest": "89e8d4c299450eb1752876867c698da5",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 31827,
"upload_time": "2011-12-05T21:12:37",
"url": "https://files.pythonhosted.org/packages/d9/37/09518dfef3ba028aacfc29ed890ea1646d8422239d1dde07875199802c5d/zc.zk-0.2.0.tar.gz"
}
],
"0.3.0": [
{
"comment_text": "",
"digests": {
"md5": "a7e03cf3bb170ad02ee264f356d4f8ab",
"sha256": "f250e8d35d8582ebbd5a3da0cab25c35924e0681dafd964b2d0fdc36a0a24627"
},
"downloads": -1,
"filename": "zc.zk-0.3.0.tar.gz",
"has_sig": false,
"md5_digest": "a7e03cf3bb170ad02ee264f356d4f8ab",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 41655,
"upload_time": "2011-12-11T19:43:27",
"url": "https://files.pythonhosted.org/packages/d1/02/3a99dad4c30b7b96b8f40d68a978b099725bb9a3b3d604941a6e603214ce/zc.zk-0.3.0.tar.gz"
}
],
"0.4.0": [
{
"comment_text": "",
"digests": {
"md5": "0a82017aba3afb0ebbf96a820296eeb8",
"sha256": "6d7f17039c8c476ae970a837398299ce3eea87912de7188e8178f39a145796b4"
},
"downloads": -1,
"filename": "zc.zk-0.4.0.tar.gz",
"has_sig": false,
"md5_digest": "0a82017aba3afb0ebbf96a820296eeb8",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 44256,
"upload_time": "2011-12-12T23:12:36",
"url": "https://files.pythonhosted.org/packages/d9/05/e1e092fb3c66cc49d09a330eb8053996a4f320d33a21e9f958d4bb81b7e7/zc.zk-0.4.0.tar.gz"
}
],
"0.5.0": [
{
"comment_text": "",
"digests": {
"md5": "a426ed1a2764f1085c65074ceac582ea",
"sha256": "154c4336adc114b5b2c3b593745944bfa6950f9ef05bb07caf47d110583b6dfc"
},
"downloads": -1,
"filename": "zc.zk-0.5.0.tar.gz",
"has_sig": false,
"md5_digest": "a426ed1a2764f1085c65074ceac582ea",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 47571,
"upload_time": "2011-12-27T18:52:30",
"url": "https://files.pythonhosted.org/packages/99/e7/fdd55e514d459eecf5e12fc1ff1e77e7a69a3bde144bfb8036715793cfc8/zc.zk-0.5.0.tar.gz"
}
],
"0.5.1": [
{
"comment_text": "",
"digests": {
"md5": "b7de62b1bc6fa4b56d111b47593444db",
"sha256": "d38622a8d568ce9d64576e3a162a15925ffa70b31eaaece8c356a2b808584557"
},
"downloads": -1,
"filename": "zc.zk-0.5.1.tar.gz",
"has_sig": false,
"md5_digest": "b7de62b1bc6fa4b56d111b47593444db",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 48847,
"upload_time": "2012-01-04T21:50:44",
"url": "https://files.pythonhosted.org/packages/db/3d/8fc973263142e954c859f503fdcfbb2714c99f5f160d4e6601fd681dddcc/zc.zk-0.5.1.tar.gz"
}
],
"0.5.2": [
{
"comment_text": "",
"digests": {
"md5": "38d4033b861b7467e7151ab2824f31f8",
"sha256": "2bdda89ca8c4452b61dbfa4f720bbb9d6a45760f39ee99b74d62ab763152fc69"
},
"downloads": -1,
"filename": "zc.zk-0.5.2.tar.gz",
"has_sig": false,
"md5_digest": "38d4033b861b7467e7151ab2824f31f8",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 49990,
"upload_time": "2012-01-06T23:45:22",
"url": "https://files.pythonhosted.org/packages/93/15/15e1e896ce8796be9e305273b3f40524c0c59beb2e3a3d95434cc6541df6/zc.zk-0.5.2.tar.gz"
}
],
"0.6.0": [
{
"comment_text": "",
"digests": {
"md5": "ab6e4973b5ac00abc9ad9ca1aabc0a7e",
"sha256": "c935fadfa41d82c8cd8007462b11a2e6113a228bd1354060ff14feafc2dec4aa"
},
"downloads": -1,
"filename": "zc.zk-0.6.0.tar.gz",
"has_sig": false,
"md5_digest": "ab6e4973b5ac00abc9ad9ca1aabc0a7e",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 54404,
"upload_time": "2012-01-25T22:59:46",
"url": "https://files.pythonhosted.org/packages/6c/7b/4c6497a84f7e85f66a4e688ac3ffea75a744309c451d20d312c47cf3aa77/zc.zk-0.6.0.tar.gz"
}
],
"0.7.0": [
{
"comment_text": "",
"digests": {
"md5": "907330782653c3a6d1c3a93521359de5",
"sha256": "6ef133abc7491ce271e4aa99cdf96150d5575a417f44a34f0f3213a537b1b09a"
},
"downloads": -1,
"filename": "zc.zk-0.7.0.tar.gz",
"has_sig": false,
"md5_digest": "907330782653c3a6d1c3a93521359de5",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 56422,
"upload_time": "2012-01-27T22:52:11",
"url": "https://files.pythonhosted.org/packages/b6/af/c4f5ba4d0a4b3ce259d1526ebe939b40ef259989fc505af50e7f2d5588a4/zc.zk-0.7.0.tar.gz"
}
],
"0.8.0": [
{
"comment_text": "",
"digests": {
"md5": "3927086d555657dcd5aec0ce440af9d5",
"sha256": "f95f9263648aea6c9b3fead8d58f8427f855b063890f48ca7724b15b06773a99"
},
"downloads": -1,
"filename": "zc.zk-0.8.0.tar.gz",
"has_sig": false,
"md5_digest": "3927086d555657dcd5aec0ce440af9d5",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 57974,
"upload_time": "2012-05-15T20:24:13",
"url": "https://files.pythonhosted.org/packages/bc/0d/1bd7d4b5f52f34ab427db02a5c44db320b768e7005895b07883899f19238/zc.zk-0.8.0.tar.gz"
}
],
"0.9.0": [
{
"comment_text": "",
"digests": {
"md5": "d5593d87adbb67115dac82b9d1be04b0",
"sha256": "693841c4aa0f9ccadd9f4c6668d3592eac84bdca6bea194f3190088dae7236a2"
},
"downloads": -1,
"filename": "zc.zk-0.9.0.tar.gz",
"has_sig": false,
"md5_digest": "d5593d87adbb67115dac82b9d1be04b0",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 59355,
"upload_time": "2012-06-26T01:08:26",
"url": "https://files.pythonhosted.org/packages/42/6a/e9723a7b2a0e270e1067e81522334fd4bf1a80d2691982d178eed0122765/zc.zk-0.9.0.tar.gz"
}
],
"0.9.1": [
{
"comment_text": "",
"digests": {
"md5": "e5471c3da64cadfaa0d4ba0d1f5a403f",
"sha256": "62ff39aadf8e477f705b3e47297eacdc19baaaa6b985a2681f65745160aa99c1"
},
"downloads": -1,
"filename": "zc.zk-0.9.1.tar.gz",
"has_sig": false,
"md5_digest": "e5471c3da64cadfaa0d4ba0d1f5a403f",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 60203,
"upload_time": "2012-07-10T17:48:59",
"url": "https://files.pythonhosted.org/packages/6c/2e/acb8923c6086c3ce8212db24d89005a23bf7e458b9e8f786d69e03a352fd/zc.zk-0.9.1.tar.gz"
}
],
"0.9.2": [
{
"comment_text": "",
"digests": {
"md5": "00116885da13181f0195571a5fe8b7b5",
"sha256": "09535d5b4470e33b12d693c0b63480c926b02cb7e9e991145fcbb4cf3493d840"
},
"downloads": -1,
"filename": "zc.zk-0.9.2.tar.gz",
"has_sig": false,
"md5_digest": "00116885da13181f0195571a5fe8b7b5",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 60380,
"upload_time": "2012-08-08T23:09:07",
"url": "https://files.pythonhosted.org/packages/05/38/eba3112290e5952926b3f9712ec09124a401f6a89a470206adb9f1cddc6c/zc.zk-0.9.2.tar.gz"
}
],
"0.9.3": [
{
"comment_text": "",
"digests": {
"md5": "3a968a780d9c2956e7affe0c7030123d",
"sha256": "13fe02f66a1bbcedeb42239368b567fc54bcc3f855172d3c7c1989b9a0fde6f9"
},
"downloads": -1,
"filename": "zc.zk-0.9.3.tar.gz",
"has_sig": false,
"md5_digest": "3a968a780d9c2956e7affe0c7030123d",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 62061,
"upload_time": "2012-08-31T20:24:26",
"url": "https://files.pythonhosted.org/packages/e7/58/629171a29cd2c0966470a852aa931d168302f5f1a6dd72bdd915a1f0bd10/zc.zk-0.9.3.tar.gz"
}
],
"0.9.4": [
{
"comment_text": "",
"digests": {
"md5": "eff0810ac116c88f6f88241501fa0240",
"sha256": "1eb66f7b9db17b7a8acab24c1a065ba6175d8d1751090ef3f38796bbe6894b0d"
},
"downloads": -1,
"filename": "zc.zk-0.9.4.tar.gz",
"has_sig": false,
"md5_digest": "eff0810ac116c88f6f88241501fa0240",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 62237,
"upload_time": "2012-09-11T20:48:49",
"url": "https://files.pythonhosted.org/packages/7c/84/de37460e5afd4af6e8f3892a1b91d3f3d9ebc88ba0ddb5102d5a5c7da8b1/zc.zk-0.9.4.tar.gz"
}
],
"1.0.0": [
{
"comment_text": "",
"digests": {
"md5": "eeb9319bdf64e2736462154f81b07ac1",
"sha256": "643aa76119710d7c61742d014ed29f67c6ee2cf7f0713f0e2b1f00edf487c045"
},
"downloads": -1,
"filename": "zc.zk-1.0.0.tar.gz",
"has_sig": false,
"md5_digest": "eeb9319bdf64e2736462154f81b07ac1",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 63119,
"upload_time": "2012-09-25T19:58:46",
"url": "https://files.pythonhosted.org/packages/4d/10/88d6709a78e59d1b4311d0388d783b6ced11a4f20af985d224ea1df48bb1/zc.zk-1.0.0.tar.gz"
}
],
"1.1.0": [
{
"comment_text": "",
"digests": {
"md5": "4f9dc88d6b1ab0bb6576efe85b588990",
"sha256": "309ff5a95d3bd8f6f04afa34fca4d56385fbb49a56d95c213f2c4ad071fa879e"
},
"downloads": -1,
"filename": "zc.zk-1.1.0.tar.gz",
"has_sig": false,
"md5_digest": "4f9dc88d6b1ab0bb6576efe85b588990",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 66605,
"upload_time": "2012-11-07T17:10:51",
"url": "https://files.pythonhosted.org/packages/fc/ac/78041739711667473a07cb74ad02c9154eda0bf46ca1ec897cb3509de5e0/zc.zk-1.1.0.tar.gz"
}
],
"1.2.0": [
{
"comment_text": "",
"digests": {
"md5": "eeda398695693d711c21c03821d0074d",
"sha256": "5632273f1cbb3e73486bd3b1def1cceb26d01360c2393f2f17ed3688d6bd7843"
},
"downloads": -1,
"filename": "zc.zk-1.2.0.tar.gz",
"has_sig": false,
"md5_digest": "eeda398695693d711c21c03821d0074d",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 67953,
"upload_time": "2012-12-14T20:53:07",
"url": "https://files.pythonhosted.org/packages/c7/9e/c9a19063084f9482cb72df726f20b14c2f6d7b4aa93db99a4e9618e732ff/zc.zk-1.2.0.tar.gz"
}
],
"2.0.0": [
{
"comment_text": "",
"digests": {
"md5": "1b383b475240b521fd4a157a6324479f",
"sha256": "23da6d0e91c1e435f73c431b62a3234bf9174fdb71b4e0d138894bed13159301"
},
"downloads": -1,
"filename": "zc.zk-2.0.0.tar.gz",
"has_sig": false,
"md5_digest": "1b383b475240b521fd4a157a6324479f",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 54223,
"upload_time": "2014-06-02T19:44:40",
"url": "https://files.pythonhosted.org/packages/c3/be/93f9d46422d0676a901d25757bb99ba71ada84bab2ce2ee87b621d82a34d/zc.zk-2.0.0.tar.gz"
}
],
"2.0.0a1": [
{
"comment_text": "",
"digests": {
"md5": "fc87b699d7cc3902c66e0996f50cd73f",
"sha256": "f5beb0ce24b74f19dd832e4c35e9e893a650075f4116f6230e9414a4d71b1501"
},
"downloads": -1,
"filename": "zc.zk-2.0.0a1.tar.gz",
"has_sig": false,
"md5_digest": "fc87b699d7cc3902c66e0996f50cd73f",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 40071,
"upload_time": "2014-01-06T14:50:20",
"url": "https://files.pythonhosted.org/packages/88/9c/f8be1acbac1df35d18f3173066bb3de5d7a0850ec25f35a1f04f8d5c96b6/zc.zk-2.0.0a1.tar.gz"
}
],
"2.0.0a2": [
{
"comment_text": "",
"digests": {
"md5": "f0d4369db05533b018218ab07219d3b8",
"sha256": "ab401aacf98436066743e135189e6b81725269b03aed118d02285d43cd29b6b9"
},
"downloads": -1,
"filename": "zc.zk-2.0.0a2.tar.gz",
"has_sig": false,
"md5_digest": "f0d4369db05533b018218ab07219d3b8",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 52008,
"upload_time": "2014-01-06T15:41:43",
"url": "https://files.pythonhosted.org/packages/a4/aa/25ec182b2750880bca9053f0f599f0a62130a13f50487058c9fefe6d31a7/zc.zk-2.0.0a2.tar.gz"
}
],
"2.0.0a3": [
{
"comment_text": "",
"digests": {
"md5": "f59c9ae0d59e99561abee071161ce979",
"sha256": "793bb52fb3f346d4ef52702e3b60e0c93e324ac0de8f4e3ffb4aa843552c157c"
},
"downloads": -1,
"filename": "zc.zk-2.0.0a3.tar.gz",
"has_sig": false,
"md5_digest": "f59c9ae0d59e99561abee071161ce979",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 52395,
"upload_time": "2014-01-08T17:49:05",
"url": "https://files.pythonhosted.org/packages/2e/73/748ea83c2e01b0938a586203c5ca0d0db7661fb73d39f6192e72fd7fd030/zc.zk-2.0.0a3.tar.gz"
}
],
"2.0.0a4": [
{
"comment_text": "",
"digests": {
"md5": "dd3d57943d84c1c2d01a57a61eb715ea",
"sha256": "591b8c0a35f963b02ab7a4406a62f5e1931b960c05ca13e15d55770093d0ba6a"
},
"downloads": -1,
"filename": "zc.zk-2.0.0a4.tar.gz",
"has_sig": false,
"md5_digest": "dd3d57943d84c1c2d01a57a61eb715ea",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 52958,
"upload_time": "2014-01-13T19:37:07",
"url": "https://files.pythonhosted.org/packages/4d/69/e65e9bdfa55ba08f2d5f82a10929d3dd9909efa27eb31acaf8bc4edf8c97/zc.zk-2.0.0a4.tar.gz"
}
],
"2.0.0a5": [
{
"comment_text": "",
"digests": {
"md5": "9ed93c7509a9609c2ec59257af11a8d0",
"sha256": "7d9e2f5e5bb6827475b0a2c005f6a42d7f8e825f467ca9ea5af55d889b8000d2"
},
"downloads": -1,
"filename": "zc.zk-2.0.0a5.tar.gz",
"has_sig": false,
"md5_digest": "9ed93c7509a9609c2ec59257af11a8d0",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 53777,
"upload_time": "2014-01-30T14:27:21",
"url": "https://files.pythonhosted.org/packages/04/c3/f262c89e78c60047e3349be02c19a01c2a7eed22f507c28baab41060ea5e/zc.zk-2.0.0a5.tar.gz"
}
],
"2.0.0a6": [
{
"comment_text": "",
"digests": {
"md5": "d5f26d8e59a2f959e17c6f779baf6ca6",
"sha256": "90d12f9e2bd9ce8b8fa5cd2faf9e192dc9f64e2930bf6f7294f7d29ffc07ade7"
},
"downloads": -1,
"filename": "zc.zk-2.0.0a6.tar.gz",
"has_sig": false,
"md5_digest": "d5f26d8e59a2f959e17c6f779baf6ca6",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 54812,
"upload_time": "2014-02-10T22:49:23",
"url": "https://files.pythonhosted.org/packages/c2/6b/269e1ca462f57a8e527b989aac82e3f4a65f91adcf05212593763bd7b0f5/zc.zk-2.0.0a6.tar.gz"
}
],
"2.0.0a7": [
{
"comment_text": "",
"digests": {
"md5": "ce97fc902d15a99ce04e8db573de0fa5",
"sha256": "84d71f90c2e3798bbac322e80c34d9d8c23d285de3c5f52b69c5a2c835b1ada6"
},
"downloads": -1,
"filename": "zc.zk-2.0.0a7.tar.gz",
"has_sig": false,
"md5_digest": "ce97fc902d15a99ce04e8db573de0fa5",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 53965,
"upload_time": "2014-02-12T18:11:27",
"url": "https://files.pythonhosted.org/packages/0b/bb/e9df4acd30a21917d8178992df87a3fb30bef638ecc994756638d04f7d6f/zc.zk-2.0.0a7.tar.gz"
}
],
"2.0.1": [
{
"comment_text": "",
"digests": {
"md5": "63f8c87c18269bca66cdf4dd37e6d53d",
"sha256": "193f940362390acac8a394b5b32f63a99d54622dacb4d6a7c8c17b312d4fc80e"
},
"downloads": -1,
"filename": "zc.zk-2.0.1.tar.gz",
"has_sig": false,
"md5_digest": "63f8c87c18269bca66cdf4dd37e6d53d",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 55075,
"upload_time": "2014-08-28T15:31:08",
"url": "https://files.pythonhosted.org/packages/98/c1/689a93ea4d2b59c9802d86e591bb5c8edfd33a87bbffba1f376e25defb4e/zc.zk-2.0.1.tar.gz"
}
],
"2.1.0": [
{
"comment_text": "",
"digests": {
"md5": "c51296793109ae7ff953edfc962d8ef5",
"sha256": "b6c2bc165082dfba56fe04804c1b66209c3c30157413e41a6172b1caf9c2da49"
},
"downloads": -1,
"filename": "zc.zk-2.1.0.tar.gz",
"has_sig": false,
"md5_digest": "c51296793109ae7ff953edfc962d8ef5",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 55947,
"upload_time": "2014-10-20T15:28:41",
"url": "https://files.pythonhosted.org/packages/5a/ba/f8d632f57311d431c1ebdc3324dddd0263f35d4ca36da777dc85ff1580ac/zc.zk-2.1.0.tar.gz"
}
]
},
"urls": [
{
"comment_text": "",
"digests": {
"md5": "c51296793109ae7ff953edfc962d8ef5",
"sha256": "b6c2bc165082dfba56fe04804c1b66209c3c30157413e41a6172b1caf9c2da49"
},
"downloads": -1,
"filename": "zc.zk-2.1.0.tar.gz",
"has_sig": false,
"md5_digest": "c51296793109ae7ff953edfc962d8ef5",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 55947,
"upload_time": "2014-10-20T15:28:41",
"url": "https://files.pythonhosted.org/packages/5a/ba/f8d632f57311d431c1ebdc3324dddd0263f35d4ca36da777dc85ff1580ac/zc.zk-2.1.0.tar.gz"
}
]
}