Merge pull request #1 from morganfainberg/import
Add support for tox unit testing
This commit is contained in:
commit
a4fbd7263e
|
@ -0,0 +1,4 @@
|
|||
[DEFAULT]
|
||||
test_command=${PYTHON:-python} -m subunit.run discover -t ./ ${OS_TEST_PATH:-./ldappool/tests} $LISTOPT $IDOPTION
|
||||
test_id_option=--load-list $IDFILE
|
||||
test_list_option=--list
|
|
@ -35,12 +35,12 @@
|
|||
# ***** END LICENSE BLOCK *****
|
||||
""" LDAP Connection Pool.
|
||||
"""
|
||||
import time
|
||||
from contextlib import contextmanager
|
||||
from threading import RLock
|
||||
import time
|
||||
|
||||
from ldap.ldapobject import ReconnectLDAPObject
|
||||
import ldap
|
||||
from ldap.ldapobject import ReconnectLDAPObject
|
||||
|
||||
|
||||
class MaxConnectionReachedError(Exception):
|
||||
|
@ -54,7 +54,8 @@ class BackendError(Exception):
|
|||
|
||||
|
||||
class StateConnector(ReconnectLDAPObject):
|
||||
"""Just remembers who is connected, and if connected"""
|
||||
"""Just remembers who is connected, and if connected."""
|
||||
|
||||
def __init__(self, *args, **kw):
|
||||
ReconnectLDAPObject.__init__(self, *args, **kw)
|
||||
self.connected = False
|
||||
|
@ -117,6 +118,7 @@ class ConnectionManager(object):
|
|||
|
||||
Provides a context manager for LDAP connectors.
|
||||
"""
|
||||
|
||||
def __init__(self, uri, bind=None, passwd=None, size=10, retry_max=3,
|
||||
retry_delay=.1, use_tls=False, timeout=-1,
|
||||
connector_cls=StateConnector, use_pool=True,
|
||||
|
@ -174,7 +176,7 @@ class ConnectionManager(object):
|
|||
try:
|
||||
self._bind(conn, bind, passwd)
|
||||
return conn
|
||||
except:
|
||||
except Exception:
|
||||
self._pool.remove(conn)
|
||||
|
||||
return None
|
||||
|
@ -193,7 +195,7 @@ class ConnectionManager(object):
|
|||
conn.active = True
|
||||
|
||||
def _create_connector(self, bind, passwd):
|
||||
"""Creates a connector, binds it, and returns it
|
||||
"""Creates a connector, binds it, and returns it.
|
||||
|
||||
Args:
|
||||
- bind: login
|
||||
|
@ -214,7 +216,7 @@ class ConnectionManager(object):
|
|||
conn.timeout = self.timeout
|
||||
self._bind(conn, bind, passwd)
|
||||
connected = True
|
||||
except ldap.LDAPError, exc:
|
||||
except ldap.LDAPError as exc:
|
||||
time.sleep(self.retry_delay)
|
||||
tries += 1
|
||||
|
||||
|
@ -280,13 +282,12 @@ class ConnectionManager(object):
|
|||
|
||||
@contextmanager
|
||||
def connection(self, bind=None, passwd=None):
|
||||
"""Creates a context'ed connector, binds it, and returns it
|
||||
"""Creates a context'ed connector, binds it, and returns it.
|
||||
|
||||
Args:
|
||||
- bind: login
|
||||
- passwd: password
|
||||
"""
|
||||
|
||||
tries = 0
|
||||
conn = None
|
||||
while tries < self.retry_max:
|
||||
|
@ -315,13 +316,12 @@ class ConnectionManager(object):
|
|||
self._release_connection(conn)
|
||||
|
||||
def purge(self, bind, passwd=None):
|
||||
"""Purge a connector
|
||||
"""Purge a connector.
|
||||
|
||||
Args:
|
||||
- bind: login
|
||||
- passwd: password
|
||||
"""
|
||||
|
||||
if self.use_pool:
|
||||
return
|
||||
|
||||
|
|
|
@ -34,10 +34,10 @@
|
|||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
import unittest
|
||||
|
||||
import ldap
|
||||
|
||||
from ldappool import (ConnectionManager, StateConnector, BackendError,
|
||||
MaxConnectionReachedError)
|
||||
import ldappool
|
||||
|
||||
|
||||
def _bind(self, who='', cred='', **kw):
|
||||
|
@ -58,17 +58,17 @@ def _bind_fails2(self, who='', cred='', **kw):
|
|||
class TestLDAPConnection(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.old = StateConnector.simple_bind_s
|
||||
StateConnector.simple_bind_s = _bind
|
||||
self.old = ldappool.StateConnector.simple_bind_s
|
||||
ldappool.StateConnector.simple_bind_s = _bind
|
||||
|
||||
def tearDown(self):
|
||||
StateConnector.simple_bind_s = self.old
|
||||
ldappool.StateConnector.simple_bind_s = self.old
|
||||
|
||||
def test_connection(self):
|
||||
uri = ''
|
||||
dn = 'uid=adminuser,ou=logins,dc=mozilla'
|
||||
passwd = 'adminuser'
|
||||
cm = ConnectionManager(uri, dn, passwd, use_pool=True, size=2)
|
||||
cm = ldappool.ConnectionManager(uri, dn, passwd, use_pool=True, size=2)
|
||||
self.assertEqual(len(cm), 0)
|
||||
|
||||
with cm.connection('dn', 'pass'):
|
||||
|
@ -86,7 +86,7 @@ class TestLDAPConnection(unittest.TestCase):
|
|||
try:
|
||||
with cm.connection('dn', 'pass'):
|
||||
pass
|
||||
except MaxConnectionReachedError:
|
||||
except ldappool.MaxConnectionReachedError:
|
||||
pass
|
||||
else:
|
||||
raise AssertionError()
|
||||
|
@ -122,18 +122,18 @@ class TestLDAPConnection(unittest.TestCase):
|
|||
unbinds.append(1)
|
||||
|
||||
# the binding fails with an LDAPError
|
||||
StateConnector.simple_bind_s = _bind_fails2
|
||||
StateConnector.unbind_s = _unbind
|
||||
ldappool.StateConnector.simple_bind_s = _bind_fails2
|
||||
ldappool.StateConnector.unbind_s = _unbind
|
||||
uri = ''
|
||||
dn = 'uid=adminuser,ou=logins,dc=mozilla'
|
||||
passwd = 'adminuser'
|
||||
cm = ConnectionManager(uri, dn, passwd, use_pool=True, size=2)
|
||||
cm = ldappool.ConnectionManager(uri, dn, passwd, use_pool=True, size=2)
|
||||
self.assertEqual(len(cm), 0)
|
||||
|
||||
try:
|
||||
with cm.connection('dn', 'pass'):
|
||||
pass
|
||||
except BackendError:
|
||||
except ldappool.BackendError:
|
||||
pass
|
||||
else:
|
||||
raise AssertionError()
|
||||
|
|
|
@ -33,29 +33,34 @@
|
|||
# the terms of any one of the MPL, the GPL or the LGPL.
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
import unittest
|
||||
import threading
|
||||
import time
|
||||
import unittest
|
||||
|
||||
import ldap
|
||||
from ldappool import (ConnectionManager, StateConnector,
|
||||
MaxConnectionReachedError)
|
||||
|
||||
import ldappool
|
||||
|
||||
# patching StateConnector
|
||||
StateConnector.users = {'uid=tarek,ou=users,dc=mozilla':
|
||||
{'uidNumber': ['1'],
|
||||
'account-enabled': ['Yes'],
|
||||
'mail': ['tarek@mozilla.com'],
|
||||
'cn': ['tarek']},
|
||||
'cn=admin,dc=mozilla': {'cn': ['admin'],
|
||||
'mail': ['admin'],
|
||||
'uidNumber': ['100']}}
|
||||
ldappool.StateConnector.users = {
|
||||
'uid=tarek,ou=users,dc=mozilla':
|
||||
{'uidNumber': ['1'],
|
||||
'account-enabled': ['Yes'],
|
||||
'mail': ['tarek@mozilla.com'],
|
||||
'cn': ['tarek']},
|
||||
'cn=admin,dc=mozilla': {'cn': ['admin'],
|
||||
'mail': ['admin'],
|
||||
'uidNumber': ['100']}}
|
||||
|
||||
|
||||
def _simple_bind(self, who='', cred='', *args):
|
||||
self.connected = True
|
||||
self.who = who
|
||||
self.cred = cred
|
||||
|
||||
StateConnector.simple_bind_s = _simple_bind
|
||||
|
||||
ldappool.StateConnector.simple_bind_s = _simple_bind
|
||||
|
||||
|
||||
def _search(self, dn, *args, **kw):
|
||||
if dn in self.users:
|
||||
|
@ -69,7 +74,9 @@ def _search(self, dn, *args, **kw):
|
|||
|
||||
raise ldap.NO_SUCH_OBJECT
|
||||
|
||||
StateConnector.search_s = _search
|
||||
|
||||
ldappool.StateConnector.search_s = _search
|
||||
|
||||
|
||||
def _add(self, dn, user):
|
||||
self.users[dn] = {}
|
||||
|
@ -80,7 +87,9 @@ def _add(self, dn, user):
|
|||
|
||||
return ldap.RES_ADD, ''
|
||||
|
||||
StateConnector.add_s = _add
|
||||
|
||||
ldappool.StateConnector.add_s = _add
|
||||
|
||||
|
||||
def _modify(self, dn, user):
|
||||
if dn in self.users:
|
||||
|
@ -90,14 +99,17 @@ def _modify(self, dn, user):
|
|||
self.users[dn][key] = value
|
||||
return ldap.RES_MODIFY, ''
|
||||
|
||||
StateConnector.modify_s = _modify
|
||||
|
||||
ldappool.StateConnector.modify_s = _modify
|
||||
|
||||
|
||||
def _delete(self, dn):
|
||||
if dn in self.users:
|
||||
del self.users[dn]
|
||||
return ldap.RES_DELETE, ''
|
||||
|
||||
StateConnector.delete_s = _delete
|
||||
|
||||
ldappool.StateConnector.delete_s = _delete
|
||||
|
||||
|
||||
class LDAPWorker(threading.Thread):
|
||||
|
@ -119,13 +131,13 @@ class LDAPWorker(threading.Thread):
|
|||
class TestLDAPSQLAuth(unittest.TestCase):
|
||||
|
||||
def test_ctor_args(self):
|
||||
pool = ConnectionManager('ldap://localhost', use_tls=True)
|
||||
pool = ldappool.ConnectionManager('ldap://localhost', use_tls=True)
|
||||
self.assertEqual(pool.use_tls, True)
|
||||
|
||||
def test_pool(self):
|
||||
dn = 'uid=adminuser,ou=logins,dc=mozilla'
|
||||
passwd = 'adminuser'
|
||||
pool = ConnectionManager('ldap://localhost', dn, passwd)
|
||||
pool = ldappool.ConnectionManager('ldap://localhost', dn, passwd)
|
||||
workers = [LDAPWorker(pool) for i in range(10)]
|
||||
|
||||
for worker in workers:
|
||||
|
@ -133,16 +145,16 @@ class TestLDAPSQLAuth(unittest.TestCase):
|
|||
|
||||
for worker in workers:
|
||||
worker.join()
|
||||
self.assertEquals(len(worker.results), 10)
|
||||
self.assertEqual(len(worker.results), 10)
|
||||
cn = worker.results[0][0][1]['cn']
|
||||
self.assertEquals(cn, ['admin'])
|
||||
self.assertEqual(cn, ['admin'])
|
||||
|
||||
def test_pool_full(self):
|
||||
dn = 'uid=adminuser,ou=logins,dc=mozilla'
|
||||
passwd = 'adminuser'
|
||||
pool = ConnectionManager('ldap://localhost', dn, passwd, size=1,
|
||||
retry_delay=1., retry_max=5,
|
||||
use_pool=True)
|
||||
pool = ldappool.ConnectionManager(
|
||||
'ldap://localhost', dn, passwd, size=1, retry_delay=1.,
|
||||
retry_max=5, use_pool=True)
|
||||
|
||||
class Worker(threading.Thread):
|
||||
|
||||
|
@ -182,7 +194,7 @@ class TestLDAPSQLAuth(unittest.TestCase):
|
|||
worker1 = Worker(pool, 1.1)
|
||||
worker1.start()
|
||||
try:
|
||||
self.assertRaises(MaxConnectionReachedError, tryit)
|
||||
self.assertRaises(ldappool.MaxConnectionReachedError, tryit)
|
||||
finally:
|
||||
worker1.join()
|
||||
|
||||
|
@ -192,8 +204,8 @@ class TestLDAPSQLAuth(unittest.TestCase):
|
|||
def test_pool_cleanup(self):
|
||||
dn = 'uid=adminuser,ou=logins,dc=mozilla'
|
||||
passwd = 'adminuser'
|
||||
pool = ConnectionManager('ldap://localhost', dn, passwd, size=1,
|
||||
use_pool=True)
|
||||
pool = ldappool.ConnectionManager('ldap://localhost', dn, passwd,
|
||||
size=1, use_pool=True)
|
||||
with pool.connection('bind1') as conn: # NOQA
|
||||
pass
|
||||
|
||||
|
@ -207,8 +219,8 @@ class TestLDAPSQLAuth(unittest.TestCase):
|
|||
def test_pool_reuse(self):
|
||||
dn = 'uid=adminuser,ou=logins,dc=mozilla'
|
||||
passwd = 'adminuser'
|
||||
pool = ConnectionManager('ldap://localhost', dn, passwd,
|
||||
use_pool=True)
|
||||
pool = ldappool.ConnectionManager('ldap://localhost', dn, passwd,
|
||||
use_pool=True)
|
||||
|
||||
with pool.connection() as conn:
|
||||
self.assertTrue(conn.active)
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
python-ldap>=2.4:python_version=='2.7' # PSF
|
|
@ -0,0 +1,36 @@
|
|||
[metadata]
|
||||
name = ldappool
|
||||
summary = A simple connector pool for python-ldap.
|
||||
description-file =
|
||||
README.rst
|
||||
author = OpenStack
|
||||
home-page = https://git.openstack.org/cgit/openstack/ldappool
|
||||
classifier =
|
||||
Intended Audience :: Developers
|
||||
License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)
|
||||
Operating System :: POSIX :: Linux
|
||||
Programming Language :: Python
|
||||
Programming Language :: Python :: 2
|
||||
Programming Language :: Python :: 2.7
|
||||
|
||||
[files]
|
||||
packages =
|
||||
ldappool
|
||||
|
||||
[build_sphinx]
|
||||
source-dir = doc/source
|
||||
build-dir = doc/build
|
||||
all_files = 1
|
||||
|
||||
[upload_sphinx]
|
||||
upload-dir = doc/build/html
|
||||
|
||||
[pbr]
|
||||
warnerrors = True
|
||||
autodoc_tree_index_modules = True
|
||||
autodoc_tree_excludes =
|
||||
setup.py
|
||||
ldappool/tests/
|
||||
|
||||
[wheel]
|
||||
universal = 1
|
29
setup.py
29
setup.py
|
@ -33,25 +33,16 @@
|
|||
# the terms of any one of the MPL, the GPL or the LGPL.
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
import setuptools
|
||||
|
||||
# In python < 2.7.4, a lazy loading of package `pbr` will break
|
||||
# setuptools if some other modules registered functions in `atexit`.
|
||||
# solution from: http://bugs.python.org/issue15881#msg170215
|
||||
try:
|
||||
from setuptools import setup
|
||||
import multiprocessing # noqa
|
||||
except ImportError:
|
||||
from distutils.core import setup
|
||||
pass
|
||||
|
||||
|
||||
with open('README.rst') as f:
|
||||
README = f.read()
|
||||
|
||||
|
||||
with open('CHANGES.rst') as f:
|
||||
CHANGES = f.read()
|
||||
|
||||
|
||||
setup(name='ldappool', version='1.1',
|
||||
packages=['ldappool', 'ldappool.tests'],
|
||||
author='Mozilla Services', author_email='services-dev@mozilla.org',
|
||||
description="A connection pool for python-ldap",
|
||||
long_description=README + '\n' + CHANGES,
|
||||
url='https://github.com/mozilla-services/ldappool',
|
||||
keywords=['python-ldap', 'ldap', 'pool'],
|
||||
license="MPL")
|
||||
setuptools.setup(
|
||||
setup_requires=['pbr>=1.8'],
|
||||
pbr=True)
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
# The order of packages is significant, because pip processes them in the order
|
||||
# of appearance.
|
||||
|
||||
hacking<0.11,>=0.10.0
|
||||
flake8-docstrings==0.2.1.post1
|
||||
|
||||
coverage>=3.6
|
||||
fixtures>=1.3.1
|
||||
sphinx!=1.2.0,!=1.3b1,<1.3,>=1.1.2
|
||||
testrepository>=0.0.18
|
||||
testresources>=0.2.4
|
||||
testtools>=1.4.0
|
|
@ -0,0 +1,45 @@
|
|||
[tox]
|
||||
minversion = 1.6
|
||||
skipsdist = True
|
||||
envlist = py27,pep8,cover,doc
|
||||
|
||||
[testenv]
|
||||
usedevelop = True
|
||||
install_command = pip install -U {opts} {packages}
|
||||
setenv = VIRTUAL_ENV={envdir}
|
||||
OS_STDOUT_NOCAPTURE=False
|
||||
OS_STDERR_NOCAPTURE=False
|
||||
|
||||
deps = -r{toxinidir}/requirements.txt
|
||||
-r{toxinidir}/test-requirements.txt
|
||||
commands = find . -type f -name "*.pyc" -delete
|
||||
python setup.py testr --slowest --testr-args='{posargs}'
|
||||
whitelist_externals = find
|
||||
|
||||
[testenv:pep8]
|
||||
commands =
|
||||
flake8
|
||||
|
||||
[testenv:venv]
|
||||
commands = {posargs}
|
||||
|
||||
[testenv:cover]
|
||||
commands = python setup.py testr --coverage --testr-args='{posargs}'
|
||||
|
||||
[flake8]
|
||||
# D100: Missing docstring in public module
|
||||
# D101: Missing docstring in public class
|
||||
# D102: Missing docstring in public method
|
||||
# D103: Missing docstring in public function
|
||||
# D104: Missing docstring in public package
|
||||
# D105: Missing docstring in magic method
|
||||
# D200: One-line docstring should fit on one line with quotes
|
||||
# D210: No whitespaces allowed surrounding docstring text
|
||||
# D401: First line should be in imperative mood
|
||||
ignore = D100,D101,D102,D104,D105,D200,D210,D401
|
||||
show-source = True
|
||||
exclude = .venv,.tox,dist,doc,*egg,build
|
||||
|
||||
[testenv:docs]
|
||||
commands=
|
||||
python setup.py build_sphinx
|
Loading…
Reference in New Issue