New Upstream Snapshot - python-etcd
Ready changes
Summary
Merged new upstream version: 0.4.5+git20170622.1.b227f49 (was: 0.4.5).
Resulting package
Built on 2022-09-19T16:29 (took 2m32s)
The resulting binary packages can be installed (if you have the apt repository enabled) by running one of:
apt install -t fresh-snapshots python3-etcd
Lintian Result
Diff
diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index 3f90b7f..0000000
--- a/.gitignore
+++ /dev/null
@@ -1,15 +0,0 @@
-*.pyc
-
-.installed.cfg
-bin
-develop-eggs
-eggs
-.eggs
-.idea
-*.egg-info
-
-tmp
-build
-dist
-docs
-.coverage
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 2c3ba50..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,23 +0,0 @@
-language: python
-python:
- - "2.7"
- - "3.5"
-
-before_install:
- - ./build_etcd.sh v2.2.0
- - pip install --upgrade setuptools
-
-# command to install dependencies
-install:
- - pip install coveralls
- - pip install coverage
- - python bootstrap.py
- - bin/buildout
-
-# command to run tests
-script:
- PATH=$PATH:./etcd/bin coverage run --source=src/etcd --omit="src/etcd/tests/*" bin/test
-
-after_success: coveralls
-# Add env var to detect it during build
-env: TRAVIS=True
diff --git a/AUTHORS b/AUTHORS
index b12bda9..716c883 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -23,6 +23,7 @@ Jim Rollenhagen
John Kristensen
Joshua Conner
Lars Bahner
+Matthew Barnes
Matthias Urlichs
Michal Witkowski
Mike Place
diff --git a/MANIFEST.in b/MANIFEST.in
index 1e7e568..e34a526 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,2 +1,4 @@
+include AUTHORS
+include LICENSE.txt
include README.rst
include NEWS.txt
diff --git a/PKG-INFO b/PKG-INFO
new file mode 100644
index 0000000..2962fd2
--- /dev/null
+++ b/PKG-INFO
@@ -0,0 +1,419 @@
+Metadata-Version: 2.1
+Name: python-etcd
+Version: 0.4.5
+Summary: A python client for etcd
+Home-page: http://github.com/jplana/python-etcd
+Author: Jose Plana
+Author-email: jplana@gmail.com
+License: MIT
+Keywords: etcd raft distributed log api client
+Platform: UNKNOWN
+Classifier: Topic :: System :: Distributed Computing
+Classifier: Topic :: Software Development :: Libraries
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 3
+Classifier: Topic :: Database :: Front-Ends
+License-File: LICENSE.txt
+License-File: AUTHORS
+
+python-etcd documentation
+=========================
+
+A python client for Etcd https://github.com/coreos/etcd
+
+Official documentation: http://python-etcd.readthedocs.org/
+
+.. image:: https://travis-ci.org/jplana/python-etcd.png?branch=master
+ :target: https://travis-ci.org/jplana/python-etcd
+
+.. image:: https://coveralls.io/repos/jplana/python-etcd/badge.svg?branch=master&service=github
+ :target: https://coveralls.io/github/jplana/python-etcd?branch=master
+
+Installation
+------------
+
+Pre-requirements
+~~~~~~~~~~~~~~~~
+
+This version of python-etcd will only work correctly with the etcd server version 2.0.x or later. If you are running an older version of etcd, please use python-etcd 0.3.3 or earlier.
+
+This client is known to work with python 2.7 and with python 3.3 or above. It is not tested or expected to work in more outdated versions of python.
+
+From source
+~~~~~~~~~~~
+
+.. code:: bash
+
+ $ python setup.py install
+
+From Pypi
+~~~~~~~~~
+
+.. code:: bash
+
+ $ python3.5 -m pip install python-etcd
+
+Usage
+-----
+
+The basic methods of the client have changed compared to previous versions, to reflect the new API structure; however a compatibility layer has been maintained so that you don't necessarily need to rewrite all your existing code.
+
+Create a client object
+~~~~~~~~~~~~~~~~~~~~~~
+
+.. code:: python
+
+ import etcd
+
+ client = etcd.Client() # this will create a client against etcd server running on localhost on port 4001
+ client = etcd.Client(port=4002)
+ client = etcd.Client(host='127.0.0.1', port=4003)
+ client = etcd.Client(host=(('127.0.0.1', 4001), ('127.0.0.1', 4002), ('127.0.0.1', 4003)))
+ client = etcd.Client(host='127.0.0.1', port=4003, allow_redirect=False) # wont let you run sensitive commands on non-leader machines, default is true
+ # If you have defined a SRV record for _etcd._tcp.example.com pointing to the clients
+ client = etcd.Client(srv_domain='example.com', protocol="https")
+ # create a client against https://api.example.com:443/etcd
+ client = etcd.Client(host='api.example.com', protocol='https', port=443, version_prefix='/etcd')
+
+Write a key
+~~~~~~~~~~~
+
+.. code:: python
+
+ client.write('/nodes/n1', 1)
+ # with ttl
+ client.write('/nodes/n2', 2, ttl=4) # sets the ttl to 4 seconds
+ client.set('/nodes/n2', 1) # Equivalent, for compatibility reasons.
+
+Read a key
+~~~~~~~~~~
+
+.. code:: python
+
+ client.read('/nodes/n2').value
+ client.read('/nodes', recursive = True) #get all the values of a directory, recursively.
+ client.get('/nodes/n2').value
+
+ # raises etcd.EtcdKeyNotFound when key not found
+ try:
+ client.read('/invalid/path')
+ except etcd.EtcdKeyNotFound:
+ # do something
+ print "error"
+
+
+Delete a key
+~~~~~~~~~~~~
+
+.. code:: python
+
+ client.delete('/nodes/n1')
+
+Atomic Compare and Swap
+~~~~~~~~~~~~~~~~~~~~~~~
+
+.. code:: python
+
+ client.write('/nodes/n2', 2, prevValue = 4) # will set /nodes/n2 's value to 2 only if its previous value was 4 and
+ client.write('/nodes/n2', 2, prevExist = False) # will set /nodes/n2 's value to 2 only if the key did not exist before
+ client.write('/nodes/n2', 2, prevIndex = 30) # will set /nodes/n2 's value to 2 only if the key was last modified at index 30
+ client.test_and_set('/nodes/n2', 2, 4) #equivalent to client.write('/nodes/n2', 2, prevValue = 4)
+
+You can also atomically update a result:
+
+.. code:: python
+
+ result = client.read('/foo')
+ print(result.value) # bar
+ result.value += u'bar'
+ updated = client.update(result) # if any other client wrote '/foo' in the meantime this will fail
+ print(updated.value) # barbar
+
+Watch a key
+~~~~~~~~~~~
+
+.. code:: python
+
+ client.read('/nodes/n1', wait = True) # will wait till the key is changed, and return once its changed
+ client.read('/nodes/n1', wait = True, timeout=30) # will wait till the key is changed, and return once its changed, or exit with an exception after 30 seconds.
+ client.read('/nodes/n1', wait = True, waitIndex = 10) # get all changes on this key starting from index 10
+ client.watch('/nodes/n1') #equivalent to client.read('/nodes/n1', wait = True)
+ client.watch('/nodes/n1', index = 10)
+
+Refreshing key TTL
+~~~~~~~~~~~~~~~~~~
+
+(Since etcd 2.3.0) Keys in etcd can be refreshed without notifying current watchers.
+
+This can be achieved by setting the refresh to true when updating a TTL.
+
+You cannot update the value of a key when refreshing it.
+
+.. code:: python
+
+ client.write('/nodes/n1', 'value', ttl=30) # sets the ttl to 30 seconds
+ client.refresh('/nodes/n1', ttl=600) # refresh ttl to 600 seconds, without notifying current watchers
+
+Locking module
+~~~~~~~~~~~~~~
+
+.. code:: python
+
+ # Initialize the lock object:
+ # NOTE: this does not acquire a lock yet
+ client = etcd.Client()
+ # Or you can custom lock prefix, default is '/_locks/' if you are using HEAD
+ client = etcd.Client(lock_prefix='/my_etcd_root/_locks')
+ lock = etcd.Lock(client, 'my_lock_name')
+
+ # Use the lock object:
+ lock.acquire(blocking=True, # will block until the lock is acquired
+ lock_ttl=None) # lock will live until we release it
+ lock.is_acquired # True
+ lock.acquire(lock_ttl=60) # renew a lock
+ lock.release() # release an existing lock
+ lock.is_acquired # False
+
+ # The lock object may also be used as a context manager:
+ client = etcd.Client()
+ with etcd.Lock(client, 'customer1') as my_lock:
+ do_stuff()
+ my_lock.is_acquired # True
+ my_lock.acquire(lock_ttl=60)
+ my_lock.is_acquired # False
+
+
+Get machines in the cluster
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. code:: python
+
+ client.machines
+
+Get leader of the cluster
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. code:: python
+
+ client.leader
+
+Generate a sequential key in a directory
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. code:: python
+
+ x = client.write("/dir/name", "value", append=True)
+ print("generated key: " + x.key)
+ print("stored value: " + x.value)
+
+List contents of a directory
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. code:: python
+
+ #stick a couple values in the directory
+ client.write("/dir/name", "value1", append=True)
+ client.write("/dir/name", "value2", append=True)
+
+ directory = client.get("/dir/name")
+
+ # loop through directory children
+ for result in directory.children:
+ print(result.key + ": " + result.value)
+
+ # or just get the first child value
+ print(directory.children.next().value)
+
+Development setup
+-----------------
+
+To create a buildout,
+
+.. code:: bash
+
+ $ python bootstrap.py
+ $ bin/buildout
+
+to test you should have etcd available in your system path:
+
+.. code:: bash
+
+ $ bin/test
+
+to generate documentation,
+
+.. code:: bash
+
+ $ cd docs
+ $ make
+
+Release HOWTO
+-------------
+
+To make a release
+
+ 1) Update release date/version in NEWS.txt and setup.py
+ 2) Run 'python setup.py sdist'
+ 3) Test the generated source distribution in dist/
+ 4) Upload to PyPI: 'python setup.py sdist register upload'
+
+
+News
+====
+0.4.5
+-----
+*Release date: 3-Mar-2017*
+
+* Remove dnspython2/3 requirement
+* Change property name setter in lock
+* Fixed acl tests
+* Added version/cluster_version properties to client
+* Fixes in lock when used as context manager
+* Fixed improper usage of urllib3 exceptions
+* Minor fixes for error classes
+* In lock return modifiedIndex to watch changes
+* In lock fix context manager exception handling
+* Improvments to the documentation
+* Remove _base_uri only after refresh from cluster
+* Avoid double update of _machines_cache
+
+
+0.4.4
+-----
+*Release date: 10-Jan-2017*
+
+* Fix some tests
+* Use sys,version_info tuple, instead of named tuple
+* Improve & fix documentation
+* Fix python3 specific problem when blocking on contented lock
+* Add refresh key method
+* Add custom lock prefix support
+
+
+0.4.3
+-----
+*Release date: 14-Dec-2015*
+
+* Fix check for parameters in case of connection error
+* Python 3.5 compatibility and general python3 cleanups
+* Added authentication and module for managing ACLs
+* Added srv record-based DNS discovery
+* Fixed (again) logging of cluster id changes
+* Fixed leader lookup
+* Properly retry request on exception
+* Client: clean up open connections when deleting
+
+0.4.2
+-----
+*Release date: 8-Oct-2015*
+
+* Fixed lock documentation
+* Fixed lock sequences due to etcd 2.2 change
+* Better exception management during response processing
+* Fixed logging of cluster ID changes
+* Fixed subtree results
+* Do not check cluster ID if etcd responses don't contain the ID
+* Added a cause to EtcdConnectionFailed
+
+
+0.4.1
+-----
+*Release date: 1-Aug-2015*
+
+* Added client-side leader election
+* Added stats endpoints
+* Added logging
+* Better exception handling
+* Check for cluster ID on each request
+* Added etcd.Client.members and fixed etcd.Client.leader
+* Removed locking and election etcd support
+* Allow the use of etcd proxies with reconnections
+* Implement pop: Remove key from etc and return the corresponding value.
+* Eternal watcher can be now recursive
+* Fix etcd.Client machines
+* Do not send parameters with `None` value to etcd
+* Support ttl=0 in write.
+* Moved pyOpenSSL into test requirements.
+* Always set certificate information so redirects from http to https work.
+
+
+0.3.3
+-----
+*Release date: 12-Apr-2015*
+
+* Forward leaves_only value in get_subtree() recursive calls
+* Fix README prevExists->prevExist
+* Added configurable version_prefix
+* Added support for recursive watch
+* Better error handling support (more detailed exceptions)
+* Fixed some unreliable tests
+
+
+0.3.2
+-----
+
+*Release date: 4-Aug-2014*
+
+* Fixed generated documentation version.
+
+
+0.3.1
+-----
+
+*Release date: 4-Aug-2014*
+
+* Added consisten read option
+* Fixed timeout parameter in read()
+* Added atomic delete parameter support
+* Fixed delete behaviour
+* Added update method that allows atomic updated on results
+* Fixed checks on write()
+* Added leaves generator to EtcdResult and get_subtree for recursive fetch
+* Added etcd_index to EtcdResult
+* Changed ethernal -> eternal
+* Updated urllib3 & pyOpenSSL libraries
+* Several performance fixes
+* Better parsing of etcd_index and raft_index
+* Removed duplicated tests
+* Added several integration and unit tests
+* Use etcd v0.3.0 in travis
+* Execute test using `python setup.py test` and nose
+
+
+0.3.0
+-----
+
+*Release date: 18-Jan-2014*
+
+* API v2 support
+* Python 3.3 compatibility
+
+
+0.2.1
+-----
+
+*Release data: 30-Nov-2013*
+
+* SSL support
+* Added support for subdirectories in results.
+* Improve test
+* Added support for reconnections, allowing death node tolerance.
+
+
+0.2.0
+-----
+
+*Release date: 30-Sep-2013*
+
+* Allow fetching of multiple keys (sub-nodes)
+
+
+0.1
+---
+
+*Release date: 18-Sep-2013*
+
+* Initial release
+
+
diff --git a/README.rst b/README.rst
index 9520aad..0df9c60 100644
--- a/README.rst
+++ b/README.rst
@@ -27,6 +27,13 @@ From source
.. code:: bash
$ python setup.py install
+
+From Pypi
+~~~~~~~~~
+
+.. code:: bash
+
+ $ python3.5 -m pip install python-etcd
Usage
-----
diff --git a/bootstrap.py b/bootstrap.py
deleted file mode 100644
index 1b28969..0000000
--- a/bootstrap.py
+++ /dev/null
@@ -1,170 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2006 Zope Foundation and Contributors.
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
-# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
-# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
-# FOR A PARTICULAR PURPOSE.
-#
-##############################################################################
-"""Bootstrap a buildout-based project
-
-Simply run this script in a directory containing a buildout.cfg.
-The script accepts buildout command-line options, so you can
-use the -c option to specify an alternate configuration file.
-"""
-
-import os
-import shutil
-import sys
-import tempfile
-
-from optparse import OptionParser
-
-tmpeggs = tempfile.mkdtemp()
-
-usage = '''\
-[DESIRED PYTHON FOR BUILDOUT] bootstrap.py [options]
-
-Bootstraps a buildout-based project.
-
-Simply run this script in a directory containing a buildout.cfg, using the
-Python that you want bin/buildout to use.
-
-Note that by using --find-links to point to local resources, you can keep
-this script from going over the network.
-'''
-
-parser = OptionParser(usage=usage)
-parser.add_option("-v", "--version", help="use a specific zc.buildout version")
-
-parser.add_option("-t", "--accept-buildout-test-releases",
- dest='accept_buildout_test_releases',
- action="store_true", default=False,
- help=("Normally, if you do not specify a --version, the "
- "bootstrap script and buildout gets the newest "
- "*final* versions of zc.buildout and its recipes and "
- "extensions for you. If you use this flag, "
- "bootstrap and buildout will get the newest releases "
- "even if they are alphas or betas."))
-parser.add_option("-c", "--config-file",
- help=("Specify the path to the buildout configuration "
- "file to be used."))
-parser.add_option("-f", "--find-links",
- help=("Specify a URL to search for buildout releases"))
-
-
-options, args = parser.parse_args()
-
-######################################################################
-# load/install setuptools
-
-to_reload = False
-try:
- import pkg_resources
- import setuptools
-except ImportError:
- ez = {}
-
- try:
- from urllib.request import urlopen
- except ImportError:
- from urllib2 import urlopen
-
- # XXX use a more permanent ez_setup.py URL when available.
- exec(urlopen('https://bitbucket.org/pypa/setuptools/raw/0.7.2/ez_setup.py'
- ).read(), ez)
- setup_args = dict(to_dir=tmpeggs, download_delay=0)
- ez['use_setuptools'](**setup_args)
-
- if to_reload:
- reload(pkg_resources)
- import pkg_resources
- # This does not (always?) update the default working set. We will
- # do it.
- for path in sys.path:
- if path not in pkg_resources.working_set.entries:
- pkg_resources.working_set.add_entry(path)
-
-######################################################################
-# Install buildout
-
-ws = pkg_resources.working_set
-
-cmd = [sys.executable, '-c',
- 'from setuptools.command.easy_install import main; main()',
- '-mZqNxd', tmpeggs]
-
-find_links = os.environ.get(
- 'bootstrap-testing-find-links',
- options.find_links or
- ('http://downloads.buildout.org/'
- if options.accept_buildout_test_releases else None)
- )
-if find_links:
- cmd.extend(['-f', find_links])
-
-setuptools_path = ws.find(
- pkg_resources.Requirement.parse('setuptools')).location
-
-requirement = 'zc.buildout'
-version = options.version
-if version is None and not options.accept_buildout_test_releases:
- # Figure out the most recent final version of zc.buildout.
- import setuptools.package_index
- _final_parts = '*final-', '*final'
-
- def _final_version(parsed_version):
- for part in parsed_version:
- if (part[:1] == '*') and (part not in _final_parts):
- return False
- return True
- index = setuptools.package_index.PackageIndex(
- search_path=[setuptools_path])
- if find_links:
- index.add_find_links((find_links,))
- req = pkg_resources.Requirement.parse(requirement)
- if index.obtain(req) is not None:
- best = []
- bestv = None
- for dist in index[req.project_name]:
- distv = dist.parsed_version
- if _final_version(distv):
- if bestv is None or distv > bestv:
- best = [dist]
- bestv = distv
- elif distv == bestv:
- best.append(dist)
- if best:
- best.sort()
- version = best[-1].version
-if version:
- requirement = '=='.join((requirement, version))
-cmd.append(requirement)
-
-import subprocess
-if subprocess.call(cmd, env=dict(os.environ, PYTHONPATH=setuptools_path)) != 0:
- raise Exception(
- "Failed to execute command:\n%s",
- repr(cmd)[1:-1])
-
-######################################################################
-# Import and run buildout
-
-ws.add_entry(tmpeggs)
-ws.require(requirement)
-import zc.buildout.buildout
-
-if not [a for a in args if '=' not in a]:
- args.append('bootstrap')
-
-# if -c was provided, we push it back into args for buildout' main function
-if options.config_file is not None:
- args[0:0] = ['-c', options.config_file]
-
-zc.buildout.buildout.main(args)
-shutil.rmtree(tmpeggs)
diff --git a/build_etcd.sh b/build_etcd.sh
deleted file mode 100755
index fc31991..0000000
--- a/build_etcd.sh
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/bin/sh
-
-if [ $# -gt 0 ]
- then
- ETCD_VERSION="$1";
- else
- ETCD_VERSION="master";
-fi
-
-echo "Using ETCD version $ETCD_VERSION"
-
-git clone https://github.com/coreos/etcd.git
-cd etcd
-git checkout $ETCD_VERSION
-./build
-
-
-${TRAVIS:?"This is not a Travis build. All Done"}
-#Temporal solution to travis issue #155
-sudo rm -rf /dev/shm && sudo ln -s /run/shm /dev/shm
-echo "All Done"
diff --git a/buildout.cfg b/buildout.cfg
deleted file mode 100644
index 4de9036..0000000
--- a/buildout.cfg
+++ /dev/null
@@ -1,41 +0,0 @@
-[buildout]
-parts = python
- sphinxbuilder
- test
- coverage
-develop = .
-eggs =
- urllib3==1.7.1
- pyOpenSSL==0.13.1
- ${deps:extraeggs}
-
-[python]
-recipe = zc.recipe.egg
-interpreter = python
-eggs = ${buildout:eggs}
-
-[test]
-recipe = pbp.recipe.noserunner
-eggs = ${python:eggs}
- mock
-
-[coverage]
-recipe = pbp.recipe.noserunner
-eggs = ${test:eggs}
- coverage
-defaults = --with-coverage
- --cover-package=etcd
-
-[sphinxbuilder]
-recipe = collective.recipe.sphinxbuilder
-source = ${buildout:directory}/docs-source
-build = ${buildout:directory}/docs
-
-
-[deps:python2]
-extraeggs =
- dnspython==1.12.0
-
-[deps:python3]
-extraeggs =
- dnspython3==1.12.0
diff --git a/debian/changelog b/debian/changelog
index cc5812e..07977f3 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+python-etcd (0.4.5+git20170622.1.b227f49-1) UNRELEASED; urgency=low
+
+ * New upstream snapshot.
+
+ -- Debian Janitor <janitor@jelmer.uk> Mon, 19 Sep 2022 16:27:53 -0000
+
python-etcd (0.4.5-4) unstable; urgency=medium
* Team upload.
diff --git a/docs-source/api.rst b/docs-source/api.rst
deleted file mode 100644
index 1059eef..0000000
--- a/docs-source/api.rst
+++ /dev/null
@@ -1,12 +0,0 @@
-API Documentation
-=========================
-.. automodule:: etcd
- :members:
-.. autoclass:: Client
- :special-members:
- :members:
- :exclude-members: __weakref__
-.. autoclass:: Lock
- :special-members:
- :members:
- :exclude-members: __weakref__
diff --git a/docs-source/conf.py b/docs-source/conf.py
deleted file mode 100644
index 5148c23..0000000
--- a/docs-source/conf.py
+++ /dev/null
@@ -1,253 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import sys, os
-
-class Mock(object):
- def __init__(self, *args, **kwargs):
- pass
-
- def __call__(self, *args, **kwargs):
- return Mock()
-
- @classmethod
- def __getattr__(cls, name):
- if name in ('__file__', '__path__'):
- return '/dev/null'
- elif name[0] == name[0].upper():
- mockType = type(name, (), {})
- mockType.__module__ = __name__
- return mockType
- else:
- return Mock()
-
-MOCK_MODULES = ['urllib3']
-for mod_name in MOCK_MODULES:
- sys.modules[mod_name] = Mock()
-
-# If extensions (or modules to document with autodoc) are in another directory,
-# add these directories to sys.path here. If the directory is relative to the
-# documentation root, use os.path.abspath to make it absolute, like shown here.
-sys.path.insert(0, os.path.abspath('../src'))
-
-# -- General configuration -----------------------------------------------------
-
-# If your documentation needs a minimal Sphinx version, state it here.
-#needs_sphinx = '1.0'
-
-# Add any Sphinx extension module names here, as strings. They can be extensions
-# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
-extensions = ['sphinx.ext.autodoc']
-
-# Add any paths that contain templates here, relative to this directory.
-templates_path = ['_templates']
-
-# The suffix of source filenames.
-source_suffix = '.rst'
-
-# The encoding of source files.
-#source_encoding = 'utf-8-sig'
-
-# The master toctree document.
-master_doc = 'index'
-
-# General information about the project.
-project = u'python-etcd'
-copyright = u'2013-2015 Jose Plana, Giuseppe Lavagetto'
-
-# The version info for the project you're documenting, acts as replacement for
-# |version| and |release|, also used in various other places throughout the
-# built documents.
-#
-# The short X.Y version.
-version = '0.4'
-# The full version, including alpha/beta/rc tags.
-release = '0.4.3'
-
-# The language for content autogenerated by Sphinx. Refer to documentation
-# for a list of supported languages.
-#language = None
-
-# There are two options for replacing |today|: either, you set today to some
-# non-false value, then it is used:
-#today = ''
-# Else, today_fmt is used as the format for a strftime call.
-#today_fmt = '%B %d, %Y'
-
-# List of patterns, relative to source directory, that match files and
-# directories to ignore when looking for source files.
-exclude_patterns = ['_build']
-
-# The reST default role (used for this markup: `text`) to use for all documents.
-#default_role = None
-
-# If true, '()' will be appended to :func: etc. cross-reference text.
-#add_function_parentheses = True
-
-# If true, the current module name will be prepended to all description
-# unit titles (such as .. function::).
-#add_module_names = True
-
-# If true, sectionauthor and moduleauthor directives will be shown in the
-# output. They are ignored by default.
-#show_authors = False
-
-# The name of the Pygments (syntax highlighting) style to use.
-pygments_style = 'sphinx'
-
-# A list of ignored prefixes for module index sorting.
-#modindex_common_prefix = []
-
-
-# -- Options for HTML output ---------------------------------------------------
-
-# The theme to use for HTML and HTML Help pages. See the documentation for
-# a list of builtin themes.
-html_theme = 'sphinxdoc'
-
-# Theme options are theme-specific and customize the look and feel of a theme
-# further. For a list of options available for each theme, see the
-# documentation.
-#html_theme_options = {}
-
-# Add any paths that contain custom themes here, relative to this directory.
-#html_theme_path = []
-
-# The name for this set of Sphinx documents. If None, it defaults to
-# "<project> v<release> documentation".
-#html_title = None
-
-# A shorter title for the navigation bar. Default is the same as html_title.
-#html_short_title = None
-
-# The name of an image file (relative to this directory) to place at the top
-# of the sidebar.
-#html_logo = None
-
-# The name of an image file (within the static path) to use as favicon of the
-# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
-# pixels large.
-#html_favicon = None
-
-# Add any paths that contain custom static files (such as style sheets) here,
-# relative to this directory. They are copied after the builtin static files,
-# so a file named "default.css" will overwrite the builtin "default.css".
-html_static_path = ['_static']
-
-# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
-# using the given strftime format.
-#html_last_updated_fmt = '%b %d, %Y'
-
-# If true, SmartyPants will be used to convert quotes and dashes to
-# typographically correct entities.
-#html_use_smartypants = True
-
-# Custom sidebar templates, maps document names to template names.
-#html_sidebars = {}
-
-# Additional templates that should be rendered to pages, maps page names to
-# template names.
-#html_additional_pages = {}
-
-# If false, no module index is generated.
-#html_domain_indices = True
-
-# If false, no index is generated.
-#html_use_index = True
-
-# If true, the index is split into individual pages for each letter.
-#html_split_index = False
-
-# If true, links to the reST sources are added to the pages.
-#html_show_sourcelink = True
-
-# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
-#html_show_sphinx = True
-
-# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
-#html_show_copyright = True
-
-# If true, an OpenSearch description file will be output, and all pages will
-# contain a <link> tag referring to it. The value of this option must be the
-# base URL from which the finished HTML is served.
-#html_use_opensearch = ''
-
-# This is the file name suffix for HTML files (e.g. ".xhtml").
-#html_file_suffix = None
-
-# Output file base name for HTML help builder.
-htmlhelp_basename = 'python-etcddoc'
-
-
-# -- Options for LaTeX output --------------------------------------------------
-
-latex_elements = {
-# The paper size ('letterpaper' or 'a4paper').
-#'papersize': 'letterpaper',
-
-# The font size ('10pt', '11pt' or '12pt').
-#'pointsize': '10pt',
-
-# Additional stuff for the LaTeX preamble.
-#'preamble': '',
-}
-
-# Grouping the document tree into LaTeX files. List of tuples
-# (source start file, target name, title, author, documentclass [howto/manual]).
-latex_documents = [
- ('index', 'python-etcd.tex', u'python-etcd Documentation',
- u'Jose Plana', 'manual'),
-]
-
-# The name of an image file (relative to this directory) to place at the top of
-# the title page.
-#latex_logo = None
-
-# For "manual" documents, if this is true, then toplevel headings are parts,
-# not chapters.
-#latex_use_parts = False
-
-# If true, show page references after internal links.
-#latex_show_pagerefs = False
-
-# If true, show URL addresses after external links.
-#latex_show_urls = False
-
-# Documents to append as an appendix to all manuals.
-#latex_appendices = []
-
-# If false, no module index is generated.
-#latex_domain_indices = True
-
-
-# -- Options for manual page output --------------------------------------------
-
-# One entry per manual page. List of tuples
-# (source start file, name, description, authors, manual section).
-man_pages = [
- ('index', 'python-etcd', u'python-etcd Documentation',
- [u'Jose Plana'], 1)
-]
-
-# If true, show URL addresses after external links.
-#man_show_urls = False
-
-
-# -- Options for Texinfo output ------------------------------------------------
-
-# Grouping the document tree into Texinfo files. List of tuples
-# (source start file, target name, title, author,
-# dir menu entry, description, category)
-texinfo_documents = [
- ('index', 'python-etcd', u'python-etcd Documentation',
- u'Jose Plana', 'python-etcd', 'One line description of project.',
- 'Miscellaneous'),
-]
-
-# Documents to append as an appendix to all manuals.
-#texinfo_appendices = []
-
-# If false, no module index is generated.
-#texinfo_domain_indices = True
-
-# How to display URL addresses: 'footnote', 'no', or 'inline'.
-#texinfo_show_urls = 'footnote'
diff --git a/docs-source/index.rst b/docs-source/index.rst
deleted file mode 100644
index e35d685..0000000
--- a/docs-source/index.rst
+++ /dev/null
@@ -1,199 +0,0 @@
-Python-etcd documentation
-=========================
-
-A python client for Etcd https://github.com/coreos/etcd
-
-
-
-
-Installation
-------------
-
-Pre-requirements
-................
-
-Install etcd
-
-
-From source
-...........
-
-.. code-block:: bash
-
- $ python setup.py install
-
-
-Usage
------
-
-Create a client object
-......................
-
-.. code-block:: python
-
- import etcd
-
- client = etcd.Client() # this will create a client against etcd server running on localhost on port 4001
- client = etcd.Client(port=4002)
- client = etcd.Client(host='127.0.0.1', port=4003)
- client = etcd.Client(host='127.0.0.1', port=4003, allow_redirect=False) # wont let you run sensitive commands on non-leader machines, default is true
- client = etcd.Client(
- host='127.0.0.1',
- port=4003,
- allow_reconnect=True,
- protocol='https',)
-
-Set a key
-.........
-
-.. code-block:: python
-
- client.write('/nodes/n1', 1)
- # with ttl
- client.write('/nodes/n2', 2, ttl=4) # sets the ttl to 4 seconds
- # create only
- client.write('/nodes/n3', 'test', prevExist=False)
- # Compare and swap values atomically
- client.write('/nodes/n3', 'test2', prevValue='test1') #this fails to write
- client.write('/nodes/n3', 'test2', prevIndex=10) #this fails to write
- # mkdir
- client.write('/nodes/queue', None, dir=True)
- # Append a value to a queue dir
- client.write('/nodes/queue', 'test', append=True) #will write i.e. /nodes/queue/11
- client.write('/nodes/queue', 'test2', append=True) #will write i.e. /nodes/queue/12
-
-You can also atomically update a result:
-
-.. code:: python
-
- result = client.read('/foo')
- print(result.value) # bar
- result.value += u'bar'
- updated = client.update(result) # if any other client wrote '/foo' in the meantime this will fail
- print(updated.value) # barbar
-
-
-
-Get a key
-.........
-
-.. code-block:: python
-
- client.read('/nodes/n2').value
- #recursively read a directory
- r = client.read('/nodes', recursive=True, sorted=True)
- for child in r.children:
- print("%s: %s" % (child.key,child.value))
-
- client.read('/nodes/n2', wait=True) #Waits for a change in value in the key before returning.
- client.read('/nodes/n2', wait=True, waitIndex=10)
-
- # raises etcd.EtcdKeyNotFound when key not found
- try:
- client.read('/invalid/path')
- except etcd.EtcdKeyNotFound:
- # do something
- print "error"
-
-
-Delete a key
-............
-
-.. code-block:: python
-
- client.delete('/nodes/n1')
- client.delete('/nodes', dir=True) #spits an error if dir is not empty
- client.delete('/nodes', recursive=True) #this works recursively
-
-Locking module
-~~~~~~~~~~~~~~
-
-.. code:: python
-
- # Initialize the lock object:
- # NOTE: this does not acquire a lock yet
- client = etcd.Client()
- lock = etcd.Lock(client, 'my_lock_name')
-
- # Use the lock object:
- lock.acquire(blocking=True, # will block until the lock is acquired
- lock_ttl=None) # lock will live until we release it
- lock.is_acquired # True
- lock.acquire(lock_ttl=60) # renew a lock
- lock.release() # release an existing lock
- lock.is_acquired # False
-
- # The lock object may also be used as a context manager:
- client = etcd.Client()
- with etcd.Lock(client, 'customer1') as my_lock:
- do_stuff()
- my_lock.is_acquired # True
- my_lock.acquire(lock_ttl=60)
- my_lock.is_acquired # False
-
-
-Get machines in the cluster
-...........................
-
-.. code-block:: python
-
- client.machines
-
-
-Get leader of the cluster
-.........................
-
-.. code-block:: python
-
- client.leader
-
-
-
-
-Development setup
------------------
-
-To create a buildout,
-
-.. code-block:: bash
-
- $ python bootstrap.py
- $ bin/buildout
-
-
-to test you should have etcd available in your system path:
-
-.. code-block:: bash
-
- $ bin/test
-
-to generate documentation,
-
-.. code-block:: bash
-
- $ cd docs
- $ make
-
-
-
-Release HOWTO
--------------
-
-To make a release,
-
- 1) Update release date/version in NEWS.txt and setup.py
- 2) Run 'python setup.py sdist'
- 3) Test the generated source distribution in dist/
- 4) Upload to PyPI: 'python setup.py sdist register upload'
- 5) Increase version in setup.py (for next release)
-
-
-List of contributors at https://github.com/jplana/python-etcd/graphs/contributors
-
-Code documentation
-------------------
-
-.. toctree::
- :maxdepth: 2
-
- api.rst
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 0000000..8bfd5a1
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,4 @@
+[egg_info]
+tag_build =
+tag_date = 0
+
diff --git a/src/etcd/auth.py b/src/etcd/auth.py
index 796772d..c5c7346 100644
--- a/src/etcd/auth.py
+++ b/src/etcd/auth.py
@@ -14,13 +14,28 @@ class EtcdAuthBase(object):
self.name = name
self.uri = "{}/auth/{}s/{}".format(self.client.version_prefix,
self.entity, self.name)
+ # This will be lazily evaluated if not manually set
+ self._legacy_api = None
+
+ @property
+ def legacy_api(self):
+ if self._legacy_api is None:
+ # The auth API has changed between 2.2 and 2.3, true story!
+ major, minor, _ = map(int, self.client.version.split('.'))
+ self._legacy_api = (major < 3 and minor < 3)
+ return self._legacy_api
+
@property
def names(self):
key = "{}s".format(self.entity)
uri = "{}/auth/{}".format(self.client.version_prefix, key)
response = self.client.api_execute(uri, self.client._MGET)
- return json.loads(response.data.decode('utf-8'))[key]
+ if self.legacy_api:
+ return json.loads(response.data.decode('utf-8'))[key]
+ else:
+ return [obj[self.entity]
+ for obj in json.loads(response.data.decode('utf-8'))[key]]
def read(self):
try:
@@ -102,7 +117,16 @@ class EtcdUser(EtcdAuthBase):
def _from_net(self, data):
d = json.loads(data.decode('utf-8'))
- self.roles = d.get('roles', [])
+ roles = d.get('roles', [])
+ try:
+ self.roles = roles
+ except TypeError:
+ # with the change of API, PUT responses are different
+ # from GET reponses, which makes everything so funny.
+ # Specifically, PUT responses are the same as before...
+ if self.legacy_api:
+ raise
+ self.roles = [obj['role'] for obj in roles]
self.name = d.get('user')
def _to_net(self, prevobj=None):
diff --git a/src/etcd/client.py b/src/etcd/client.py
index 74ad8fc..7c4d985 100644
--- a/src/etcd/client.py
+++ b/src/etcd/client.py
@@ -214,12 +214,8 @@ class Client(object):
Sets the version information provided by the server.
"""
# Set the version
- version_info = json.loads(self.http.request(
- self._MGET,
- self._base_uri + '/version',
- headers=self._get_headers(),
- timeout=self.read_timeout,
- redirect=self.allow_redirect).data.decode('utf-8'))
+ data = self.api_execute('/version', self._MGET).data
+ version_info = json.loads(data.decode('utf-8'))
self._version = version_info['etcdserver']
self._cluster_version = version_info['etcdcluster']
@@ -232,7 +228,7 @@ class Client(object):
(answer.target.to_text(omit_final_dot=True), answer.port))
_log.debug("Found %s", hosts)
if not len(hosts):
- raise ValueError("The SRV record is present but no host were found")
+ raise ValueError("The SRV record is present but no hosts were found")
return tuple(hosts)
def __del__(self):
@@ -822,7 +818,7 @@ class Client(object):
def _next_server(self, cause=None):
""" Selects the next server in the list, refreshes the server list. """
- _log.debug("Selection next machine in cache. Available machines: %s",
+ _log.debug("Selecting next machine in cache. Available machines: %s",
self._machines_cache)
try:
mach = self._machines_cache.pop()
@@ -856,7 +852,7 @@ class Client(object):
# Check the cluster ID hasn't changed under us. We use
# preload_content=False above so we can read the headers
# before we wait for the content of a watch.
- self._check_cluster_id(response)
+ self._check_cluster_id(response, path)
# Now force the data to be preloaded in order to trigger any
# IO-related errors in this method rather than when we try to
# access it later.
@@ -896,7 +892,7 @@ class Client(object):
_log.warning(e)
raise
except:
- _log.exception("Unexpected request failure, re-raising.")
+ _log.debug("Unexpected request failure, re-raising.")
raise
if some_request_failed:
@@ -950,10 +946,11 @@ class Client(object):
headers=headers,
preload_content=False)
- def _check_cluster_id(self, response):
+ def _check_cluster_id(self, response, path):
cluster_id = response.getheader("x-etcd-cluster-id")
if not cluster_id:
- _log.warning("etcd response did not contain a cluster ID")
+ if self.version_prefix in path:
+ _log.warning("etcd response did not contain a cluster ID")
return
id_changed = (self.expected_cluster_id and
cluster_id != self.expected_cluster_id)
diff --git a/src/etcd/tests/test_auth.py b/src/etcd/tests/test_auth.py
index 14475f9..5c8c0b0 100644
--- a/src/etcd/tests/test_auth.py
+++ b/src/etcd/tests/test_auth.py
@@ -93,6 +93,10 @@ class EtcdUserTest(TestEtcdAuthBase):
self.assertEquals(u.roles, set(['guest', 'root']))
# set roles as a list, it works!
u.roles = ['guest', 'test_group']
+ # We need this or the new API will return an internal error
+ r = auth.EtcdRole(self.client, 'test_group')
+ r.acls = {'*': 'R', '/test/*': 'RW'}
+ r.write()
try:
u.write()
except:
diff --git a/src/etcd/tests/unit/test_client.py b/src/etcd/tests/unit/test_client.py
index b0b53b2..2981de7 100644
--- a/src/etcd/tests/unit/test_client.py
+++ b/src/etcd/tests/unit/test_client.py
@@ -3,13 +3,15 @@ import etcd
import dns.name
import dns.rdtypes.IN.SRV
import dns.resolver
+from etcd.tests.unit import TestClientApiBase
try:
import mock
except ImportError:
from unittest import mock
-class TestClient(unittest.TestCase):
+class TestClient(TestClientApiBase):
+
def test_instantiate(self):
""" client can be instantiated"""
@@ -123,55 +125,37 @@ class TestClient(unittest.TestCase):
def test__set_version_info(self):
"""Verify _set_version_info makes the proper call to the server"""
- with mock.patch('urllib3.PoolManager') as _pm:
- _request = _pm().request
- # Return the expected data type
- _request.return_value = mock.MagicMock(
- data=b'{"etcdserver": "2.2.3", "etcdcluster": "2.3.0"}')
-
- # Create the client and make the call.
- client = etcd.Client()
- client._set_version_info()
-
- # Verify we call the proper endpoint
- _request.assert_called_once_with(
- client._MGET,
- client._base_uri + '/version',
- headers=mock.ANY,
- redirect=mock.ANY,
- timeout=mock.ANY)
-
- # Verify the properties while we are here
- self.assertEquals('2.2.3', client.version)
- self.assertEquals('2.3.0', client.cluster_version)
+ data = {"etcdserver": "2.2.3", "etcdcluster": "2.3.0"}
+ self._mock_api(200, data)
+ self.client.api_execute.return_value.getheader.return_value = None
+ # Create the client and make the call.
+ self.client._set_version_info()
+
+ # Verify we call the proper endpoint
+ self.client.api_execute.assert_called_once_with(
+ '/version',
+ self.client._MGET
+ )
+ # Verify the properties while we are here
+ self.assertEquals('2.2.3', self.client.version)
+ self.assertEquals('2.3.0', self.client.cluster_version)
def test_version_property(self):
"""Ensure the version property is set on first access."""
- with mock.patch('urllib3.PoolManager') as _pm:
- _request = _pm().request
- # Return the expected data type
- _request.return_value = mock.MagicMock(
- data=b'{"etcdserver": "2.2.3", "etcdcluster": "2.3.0"}')
-
- # Create the client.
- client = etcd.Client()
+ data = {"etcdserver": "2.2.3", "etcdcluster": "2.3.0"}
+ self._mock_api(200, data)
+ self.client.api_execute.return_value.getheader.return_value = None
- # Verify the version property is set
- self.assertEquals('2.2.3', client.version)
+ # Verify the version property is set
+ self.assertEquals('2.2.3', self.client.version)
def test_cluster_version_property(self):
"""Ensure the cluster version property is set on first access."""
- with mock.patch('urllib3.PoolManager') as _pm:
- _request = _pm().request
- # Return the expected data type
- _request.return_value = mock.MagicMock(
- data=b'{"etcdserver": "2.2.3", "etcdcluster": "2.3.0"}')
-
- # Create the client.
- client = etcd.Client()
-
- # Verify the cluster_version property is set
- self.assertEquals('2.3.0', client.cluster_version)
+ data = {"etcdserver": "2.2.3", "etcdcluster": "2.3.0"}
+ self._mock_api(200, data)
+ self.client.api_execute.return_value.getheader.return_value = None
+ # Verify the cluster_version property is set
+ self.assertEquals('2.3.0', self.client.cluster_version)
def test_get_headers_without_auth(self):
client = etcd.Client()
diff --git a/src/python_etcd.egg-info/PKG-INFO b/src/python_etcd.egg-info/PKG-INFO
new file mode 100644
index 0000000..2962fd2
--- /dev/null
+++ b/src/python_etcd.egg-info/PKG-INFO
@@ -0,0 +1,419 @@
+Metadata-Version: 2.1
+Name: python-etcd
+Version: 0.4.5
+Summary: A python client for etcd
+Home-page: http://github.com/jplana/python-etcd
+Author: Jose Plana
+Author-email: jplana@gmail.com
+License: MIT
+Keywords: etcd raft distributed log api client
+Platform: UNKNOWN
+Classifier: Topic :: System :: Distributed Computing
+Classifier: Topic :: Software Development :: Libraries
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 3
+Classifier: Topic :: Database :: Front-Ends
+License-File: LICENSE.txt
+License-File: AUTHORS
+
+python-etcd documentation
+=========================
+
+A python client for Etcd https://github.com/coreos/etcd
+
+Official documentation: http://python-etcd.readthedocs.org/
+
+.. image:: https://travis-ci.org/jplana/python-etcd.png?branch=master
+ :target: https://travis-ci.org/jplana/python-etcd
+
+.. image:: https://coveralls.io/repos/jplana/python-etcd/badge.svg?branch=master&service=github
+ :target: https://coveralls.io/github/jplana/python-etcd?branch=master
+
+Installation
+------------
+
+Pre-requirements
+~~~~~~~~~~~~~~~~
+
+This version of python-etcd will only work correctly with the etcd server version 2.0.x or later. If you are running an older version of etcd, please use python-etcd 0.3.3 or earlier.
+
+This client is known to work with python 2.7 and with python 3.3 or above. It is not tested or expected to work in more outdated versions of python.
+
+From source
+~~~~~~~~~~~
+
+.. code:: bash
+
+ $ python setup.py install
+
+From Pypi
+~~~~~~~~~
+
+.. code:: bash
+
+ $ python3.5 -m pip install python-etcd
+
+Usage
+-----
+
+The basic methods of the client have changed compared to previous versions, to reflect the new API structure; however a compatibility layer has been maintained so that you don't necessarily need to rewrite all your existing code.
+
+Create a client object
+~~~~~~~~~~~~~~~~~~~~~~
+
+.. code:: python
+
+ import etcd
+
+ client = etcd.Client() # this will create a client against etcd server running on localhost on port 4001
+ client = etcd.Client(port=4002)
+ client = etcd.Client(host='127.0.0.1', port=4003)
+ client = etcd.Client(host=(('127.0.0.1', 4001), ('127.0.0.1', 4002), ('127.0.0.1', 4003)))
+ client = etcd.Client(host='127.0.0.1', port=4003, allow_redirect=False) # wont let you run sensitive commands on non-leader machines, default is true
+ # If you have defined a SRV record for _etcd._tcp.example.com pointing to the clients
+ client = etcd.Client(srv_domain='example.com', protocol="https")
+ # create a client against https://api.example.com:443/etcd
+ client = etcd.Client(host='api.example.com', protocol='https', port=443, version_prefix='/etcd')
+
+Write a key
+~~~~~~~~~~~
+
+.. code:: python
+
+ client.write('/nodes/n1', 1)
+ # with ttl
+ client.write('/nodes/n2', 2, ttl=4) # sets the ttl to 4 seconds
+ client.set('/nodes/n2', 1) # Equivalent, for compatibility reasons.
+
+Read a key
+~~~~~~~~~~
+
+.. code:: python
+
+ client.read('/nodes/n2').value
+ client.read('/nodes', recursive = True) #get all the values of a directory, recursively.
+ client.get('/nodes/n2').value
+
+ # raises etcd.EtcdKeyNotFound when key not found
+ try:
+ client.read('/invalid/path')
+ except etcd.EtcdKeyNotFound:
+ # do something
+ print "error"
+
+
+Delete a key
+~~~~~~~~~~~~
+
+.. code:: python
+
+ client.delete('/nodes/n1')
+
+Atomic Compare and Swap
+~~~~~~~~~~~~~~~~~~~~~~~
+
+.. code:: python
+
+ client.write('/nodes/n2', 2, prevValue = 4) # will set /nodes/n2 's value to 2 only if its previous value was 4 and
+ client.write('/nodes/n2', 2, prevExist = False) # will set /nodes/n2 's value to 2 only if the key did not exist before
+ client.write('/nodes/n2', 2, prevIndex = 30) # will set /nodes/n2 's value to 2 only if the key was last modified at index 30
+ client.test_and_set('/nodes/n2', 2, 4) #equivalent to client.write('/nodes/n2', 2, prevValue = 4)
+
+You can also atomically update a result:
+
+.. code:: python
+
+ result = client.read('/foo')
+ print(result.value) # bar
+ result.value += u'bar'
+ updated = client.update(result) # if any other client wrote '/foo' in the meantime this will fail
+ print(updated.value) # barbar
+
+Watch a key
+~~~~~~~~~~~
+
+.. code:: python
+
+ client.read('/nodes/n1', wait = True) # will wait till the key is changed, and return once its changed
+ client.read('/nodes/n1', wait = True, timeout=30) # will wait till the key is changed, and return once its changed, or exit with an exception after 30 seconds.
+ client.read('/nodes/n1', wait = True, waitIndex = 10) # get all changes on this key starting from index 10
+ client.watch('/nodes/n1') #equivalent to client.read('/nodes/n1', wait = True)
+ client.watch('/nodes/n1', index = 10)
+
+Refreshing key TTL
+~~~~~~~~~~~~~~~~~~
+
+(Since etcd 2.3.0) Keys in etcd can be refreshed without notifying current watchers.
+
+This can be achieved by setting the refresh to true when updating a TTL.
+
+You cannot update the value of a key when refreshing it.
+
+.. code:: python
+
+ client.write('/nodes/n1', 'value', ttl=30) # sets the ttl to 30 seconds
+ client.refresh('/nodes/n1', ttl=600) # refresh ttl to 600 seconds, without notifying current watchers
+
+Locking module
+~~~~~~~~~~~~~~
+
+.. code:: python
+
+ # Initialize the lock object:
+ # NOTE: this does not acquire a lock yet
+ client = etcd.Client()
+ # Or you can custom lock prefix, default is '/_locks/' if you are using HEAD
+ client = etcd.Client(lock_prefix='/my_etcd_root/_locks')
+ lock = etcd.Lock(client, 'my_lock_name')
+
+ # Use the lock object:
+ lock.acquire(blocking=True, # will block until the lock is acquired
+ lock_ttl=None) # lock will live until we release it
+ lock.is_acquired # True
+ lock.acquire(lock_ttl=60) # renew a lock
+ lock.release() # release an existing lock
+ lock.is_acquired # False
+
+ # The lock object may also be used as a context manager:
+ client = etcd.Client()
+ with etcd.Lock(client, 'customer1') as my_lock:
+ do_stuff()
+ my_lock.is_acquired # True
+ my_lock.acquire(lock_ttl=60)
+ my_lock.is_acquired # False
+
+
+Get machines in the cluster
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. code:: python
+
+ client.machines
+
+Get leader of the cluster
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. code:: python
+
+ client.leader
+
+Generate a sequential key in a directory
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. code:: python
+
+ x = client.write("/dir/name", "value", append=True)
+ print("generated key: " + x.key)
+ print("stored value: " + x.value)
+
+List contents of a directory
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. code:: python
+
+ #stick a couple values in the directory
+ client.write("/dir/name", "value1", append=True)
+ client.write("/dir/name", "value2", append=True)
+
+ directory = client.get("/dir/name")
+
+ # loop through directory children
+ for result in directory.children:
+ print(result.key + ": " + result.value)
+
+ # or just get the first child value
+ print(directory.children.next().value)
+
+Development setup
+-----------------
+
+To create a buildout,
+
+.. code:: bash
+
+ $ python bootstrap.py
+ $ bin/buildout
+
+to test you should have etcd available in your system path:
+
+.. code:: bash
+
+ $ bin/test
+
+to generate documentation,
+
+.. code:: bash
+
+ $ cd docs
+ $ make
+
+Release HOWTO
+-------------
+
+To make a release
+
+ 1) Update release date/version in NEWS.txt and setup.py
+ 2) Run 'python setup.py sdist'
+ 3) Test the generated source distribution in dist/
+ 4) Upload to PyPI: 'python setup.py sdist register upload'
+
+
+News
+====
+0.4.5
+-----
+*Release date: 3-Mar-2017*
+
+* Remove dnspython2/3 requirement
+* Change property name setter in lock
+* Fixed acl tests
+* Added version/cluster_version properties to client
+* Fixes in lock when used as context manager
+* Fixed improper usage of urllib3 exceptions
+* Minor fixes for error classes
+* In lock return modifiedIndex to watch changes
+* In lock fix context manager exception handling
+* Improvments to the documentation
+* Remove _base_uri only after refresh from cluster
+* Avoid double update of _machines_cache
+
+
+0.4.4
+-----
+*Release date: 10-Jan-2017*
+
+* Fix some tests
+* Use sys,version_info tuple, instead of named tuple
+* Improve & fix documentation
+* Fix python3 specific problem when blocking on contented lock
+* Add refresh key method
+* Add custom lock prefix support
+
+
+0.4.3
+-----
+*Release date: 14-Dec-2015*
+
+* Fix check for parameters in case of connection error
+* Python 3.5 compatibility and general python3 cleanups
+* Added authentication and module for managing ACLs
+* Added srv record-based DNS discovery
+* Fixed (again) logging of cluster id changes
+* Fixed leader lookup
+* Properly retry request on exception
+* Client: clean up open connections when deleting
+
+0.4.2
+-----
+*Release date: 8-Oct-2015*
+
+* Fixed lock documentation
+* Fixed lock sequences due to etcd 2.2 change
+* Better exception management during response processing
+* Fixed logging of cluster ID changes
+* Fixed subtree results
+* Do not check cluster ID if etcd responses don't contain the ID
+* Added a cause to EtcdConnectionFailed
+
+
+0.4.1
+-----
+*Release date: 1-Aug-2015*
+
+* Added client-side leader election
+* Added stats endpoints
+* Added logging
+* Better exception handling
+* Check for cluster ID on each request
+* Added etcd.Client.members and fixed etcd.Client.leader
+* Removed locking and election etcd support
+* Allow the use of etcd proxies with reconnections
+* Implement pop: Remove key from etc and return the corresponding value.
+* Eternal watcher can be now recursive
+* Fix etcd.Client machines
+* Do not send parameters with `None` value to etcd
+* Support ttl=0 in write.
+* Moved pyOpenSSL into test requirements.
+* Always set certificate information so redirects from http to https work.
+
+
+0.3.3
+-----
+*Release date: 12-Apr-2015*
+
+* Forward leaves_only value in get_subtree() recursive calls
+* Fix README prevExists->prevExist
+* Added configurable version_prefix
+* Added support for recursive watch
+* Better error handling support (more detailed exceptions)
+* Fixed some unreliable tests
+
+
+0.3.2
+-----
+
+*Release date: 4-Aug-2014*
+
+* Fixed generated documentation version.
+
+
+0.3.1
+-----
+
+*Release date: 4-Aug-2014*
+
+* Added consisten read option
+* Fixed timeout parameter in read()
+* Added atomic delete parameter support
+* Fixed delete behaviour
+* Added update method that allows atomic updated on results
+* Fixed checks on write()
+* Added leaves generator to EtcdResult and get_subtree for recursive fetch
+* Added etcd_index to EtcdResult
+* Changed ethernal -> eternal
+* Updated urllib3 & pyOpenSSL libraries
+* Several performance fixes
+* Better parsing of etcd_index and raft_index
+* Removed duplicated tests
+* Added several integration and unit tests
+* Use etcd v0.3.0 in travis
+* Execute test using `python setup.py test` and nose
+
+
+0.3.0
+-----
+
+*Release date: 18-Jan-2014*
+
+* API v2 support
+* Python 3.3 compatibility
+
+
+0.2.1
+-----
+
+*Release data: 30-Nov-2013*
+
+* SSL support
+* Added support for subdirectories in results.
+* Improve test
+* Added support for reconnections, allowing death node tolerance.
+
+
+0.2.0
+-----
+
+*Release date: 30-Sep-2013*
+
+* Allow fetching of multiple keys (sub-nodes)
+
+
+0.1
+---
+
+*Release date: 18-Sep-2013*
+
+* Initial release
+
+
diff --git a/src/python_etcd.egg-info/SOURCES.txt b/src/python_etcd.egg-info/SOURCES.txt
new file mode 100644
index 0000000..443d6ca
--- /dev/null
+++ b/src/python_etcd.egg-info/SOURCES.txt
@@ -0,0 +1,28 @@
+AUTHORS
+LICENSE.txt
+MANIFEST.in
+NEWS.txt
+README.rst
+setup.py
+src/etcd/__init__.py
+src/etcd/auth.py
+src/etcd/client.py
+src/etcd/lock.py
+src/etcd/tests/__init__.py
+src/etcd/tests/test_auth.py
+src/etcd/tests/integration/__init__.py
+src/etcd/tests/integration/helpers.py
+src/etcd/tests/integration/test_simple.py
+src/etcd/tests/integration/test_ssl.py
+src/etcd/tests/unit/__init__.py
+src/etcd/tests/unit/test_client.py
+src/etcd/tests/unit/test_lock.py
+src/etcd/tests/unit/test_old_request.py
+src/etcd/tests/unit/test_request.py
+src/etcd/tests/unit/test_result.py
+src/python_etcd.egg-info/PKG-INFO
+src/python_etcd.egg-info/SOURCES.txt
+src/python_etcd.egg-info/dependency_links.txt
+src/python_etcd.egg-info/not-zip-safe
+src/python_etcd.egg-info/requires.txt
+src/python_etcd.egg-info/top_level.txt
\ No newline at end of file
diff --git a/src/python_etcd.egg-info/dependency_links.txt b/src/python_etcd.egg-info/dependency_links.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/src/python_etcd.egg-info/dependency_links.txt
@@ -0,0 +1 @@
+
diff --git a/src/python_etcd.egg-info/not-zip-safe b/src/python_etcd.egg-info/not-zip-safe
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/src/python_etcd.egg-info/not-zip-safe
@@ -0,0 +1 @@
+
diff --git a/src/python_etcd.egg-info/requires.txt b/src/python_etcd.egg-info/requires.txt
new file mode 100644
index 0000000..bac8bca
--- /dev/null
+++ b/src/python_etcd.egg-info/requires.txt
@@ -0,0 +1,2 @@
+dnspython>=1.13.0
+urllib3>=1.7.1
diff --git a/src/python_etcd.egg-info/top_level.txt b/src/python_etcd.egg-info/top_level.txt
new file mode 100644
index 0000000..7119fa0
--- /dev/null
+++ b/src/python_etcd.egg-info/top_level.txt
@@ -0,0 +1 @@
+etcd
Debdiff
File lists identical (after any substitutions)
No differences were encountered in the control files