Skip to content

Commit 02915b3

Browse files
committed
slapdtest: Set and check command paths in __init__
Make SlapdObject.PATH_* instance attributes, rather than class ones. Set them in __init__. Move checking them from start() to __init__(), so the check becomes simply error handling. Put a straight-up copy of Python's shutil.which() in ldap.compat -- it is a temporary backport, not a modified fork.
1 parent f01b66e commit 02915b3

File tree

2 files changed

+113
-66
lines changed

2 files changed

+113
-66
lines changed

Lib/ldap/compat.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""Compatibility wrappers for Py2/Py3."""
22

33
import sys
4+
import os
45

56
if sys.version_info[0] < 3:
67
from UserDict import UserDict, IterableUserDict
@@ -41,3 +42,72 @@ def reraise(exc_type, exc_value, exc_traceback):
4142
"""
4243
# In Python 3, all exception info is contained in one object.
4344
raise exc_value
45+
46+
try:
47+
from shutil import which
48+
except ImportError:
49+
# shutil.which() from Python 3.6
50+
# "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
51+
# 2011, 2012, 2013, 2014, 2015, 2016, 2017 Python Software Foundation;
52+
# All Rights Reserved"
53+
def which(cmd, mode=os.F_OK | os.X_OK, path=None):
54+
"""Given a command, mode, and a PATH string, return the path which
55+
conforms to the given mode on the PATH, or None if there is no such
56+
file.
57+
58+
`mode` defaults to os.F_OK | os.X_OK. `path` defaults to the result
59+
of os.environ.get("PATH"), or can be overridden with a custom search
60+
path.
61+
62+
"""
63+
# Check that a given file can be accessed with the correct mode.
64+
# Additionally check that `file` is not a directory, as on Windows
65+
# directories pass the os.access check.
66+
def _access_check(fn, mode):
67+
return (os.path.exists(fn) and os.access(fn, mode)
68+
and not os.path.isdir(fn))
69+
70+
# If we're given a path with a directory part, look it up directly rather
71+
# than referring to PATH directories. This includes checking relative to the
72+
# current directory, e.g. ./script
73+
if os.path.dirname(cmd):
74+
if _access_check(cmd, mode):
75+
return cmd
76+
return None
77+
78+
if path is None:
79+
path = os.environ.get("PATH", os.defpath)
80+
if not path:
81+
return None
82+
path = path.split(os.pathsep)
83+
84+
if sys.platform == "win32":
85+
# The current directory takes precedence on Windows.
86+
if not os.curdir in path:
87+
path.insert(0, os.curdir)
88+
89+
# PATHEXT is necessary to check on Windows.
90+
pathext = os.environ.get("PATHEXT", "").split(os.pathsep)
91+
# See if the given file matches any of the expected path extensions.
92+
# This will allow us to short circuit when given "python.exe".
93+
# If it does match, only test that one, otherwise we have to try
94+
# others.
95+
if any(cmd.lower().endswith(ext.lower()) for ext in pathext):
96+
files = [cmd]
97+
else:
98+
files = [cmd + ext for ext in pathext]
99+
else:
100+
# On other platforms you don't have things like PATHEXT to tell you
101+
# what file suffixes are executable, so just pass on cmd as-is.
102+
files = [cmd]
103+
104+
seen = set()
105+
for dir in path:
106+
normdir = os.path.normcase(dir)
107+
if not normdir in seen:
108+
seen.add(normdir)
109+
for thefile in files:
110+
name = os.path.join(dir, thefile)
111+
if _access_check(name, mode):
112+
return name
113+
return None

Lib/slapdtest/_slapdtest.py

Lines changed: 43 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
os.environ['LDAPNOINIT'] = '1'
2222

2323
import ldap
24-
from ldap.compat import quote_plus
24+
from ldap.compat import quote_plus, which
2525

2626
HERE = os.path.abspath(os.path.dirname(__file__))
2727

@@ -109,46 +109,14 @@ def requires_ldapi():
109109
else:
110110
return identity
111111

112-
113-
def _which(cmd):
114-
"""Specialized which command based on shutil.which() from Python 3.6.
115-
116-
* simplified
117-
* always adds /sbin directories to path
118-
"""
119-
120-
def _access_check(fn):
121-
return (os.path.exists(fn) and os.access(fn, os.F_OK | os.X_OK)
122-
and not os.path.isdir(fn))
123-
124-
# Path with directory part skips PATH lookup.
125-
if os.path.dirname(cmd):
126-
if _access_check(cmd):
127-
return cmd
128-
return None
129-
130-
path = os.environ.get("PATH", os.defpath).split(os.pathsep)
131-
132-
if sys.platform == 'win32':
133-
if os.curdir not in path:
134-
path.insert(0, os.curdir)
135-
# include path extension (.exe)
136-
pathext = os.environ.get("PATHEXT", "").split(os.pathsep)
137-
files = [cmd + ext for ext in pathext]
138-
else:
139-
# always include sbin for slapd binary
140-
for sbin in ['/usr/local/sbin', '/sbin', '/usr/sbin']:
141-
if sbin not in path:
142-
path.append(sbin)
143-
files = [cmd]
144-
145-
for directory in path:
146-
for name in files:
147-
name = os.path.join(directory, name)
148-
if _access_check(name):
149-
return name
150-
return None
151-
112+
def _add_sbin(path):
113+
"""Add /sbin and related directories to a command search path"""
114+
directories = path.split(os.pathsep)
115+
if sys.platform != 'win32':
116+
for sbin in '/usr/local/sbin', '/sbin', '/usr/sbin':
117+
if sbin not in directories:
118+
directories.append(sbin)
119+
return os.pathsep.join(directories)
152120

153121
def combined_logger(
154122
log_name,
@@ -221,14 +189,9 @@ class SlapdObject(object):
221189
SCHEMADIR = "/etc/ldap/schema"
222190
else:
223191
SCHEMADIR = None
224-
# _check_requirements turns paths into absolute paths
225-
PATH_LDAPADD = 'ldapadd'
226-
PATH_LDAPDELETE = 'ldapdelete'
227-
PATH_LDAPMODIFY = 'ldapmodify'
228-
PATH_LDAPWHOAMI = 'ldapwhoami'
229-
# The following two binaries are usually in /usr/sbin.
230-
PATH_SLAPD = os.environ.get('SLAPD', 'slapd')
231-
PATH_SLAPTEST = 'slaptest'
192+
193+
BIN_PATH = os.environ.get('BIN', os.environ.get('PATH', os.defpath))
194+
SBIN_PATH = os.environ.get('SBIN', _add_sbin(BIN_PATH))
232195

233196
# time in secs to wait before trying to access slapd via LDAP (again)
234197
_start_sleep = 1.5
@@ -256,29 +219,44 @@ def __init__(self):
256219
self.default_ldap_uri = self.ldap_uri
257220
# Use simple bind via LDAP uri
258221
self.cli_sasl_external = False
222+
223+
self._find_commands()
224+
225+
if self.SCHEMADIR is None:
226+
raise ValueError('SCHEMADIR is None, ldap schemas are missing.')
227+
259228
# TLS certs
260229
self.cafile = os.path.join(HERE, 'certs/ca.pem')
261230
self.servercert = os.path.join(HERE, 'certs/server.pem')
262231
self.serverkey = os.path.join(HERE, 'certs/server.key')
263232
self.clientcert = os.path.join(HERE, 'certs/client.pem')
264233
self.clientkey = os.path.join(HERE, 'certs/client.key')
265234

266-
def _check_requirements(self):
267-
names = [
268-
"PATH_LDAPADD", "PATH_LDAPMODIFY", "PATH_LDAPDELETE",
269-
"PATH_LDAPWHOAMI", "PATH_SLAPD", "PATH_SLAPTEST",
270-
]
271-
for name in names:
272-
value = getattr(self, name)
273-
binary = _which(value)
274-
if binary is None:
275-
raise ValueError(
276-
"Command '{}' not found in PATH".format(value)
277-
)
278-
else:
279-
setattr(self, name, binary)
280-
if self.SCHEMADIR is None:
281-
raise ValueError('SCHEMADIR is None, ldap schemas are missing.')
235+
def _find_commands(self):
236+
self.PATH_LDAPADD = self._find_command('ldapadd')
237+
self.PATH_LDAPDELETE = self._find_command('ldapdelete')
238+
self.PATH_LDAPMODIFY = self._find_command('ldapmodify')
239+
self.PATH_LDAPWHOAMI = self._find_command('ldapwhoami')
240+
241+
self.PATH_SLAPD = os.environ.get('SLAPD', None)
242+
if not self.PATH_SLAPD:
243+
self.PATH_SLAPD = self._find_command('slapd', in_sbin=True)
244+
self.PATH_SLAPTEST = self._find_command('slaptest', in_sbin=True)
245+
246+
def _find_command(self, cmd, in_sbin=False):
247+
if in_sbin:
248+
path = self.SBIN_PATH
249+
var_name = 'SBIN'
250+
else:
251+
path = self.BIN_PATH
252+
var_name = 'BIN'
253+
command = which(cmd, path=path)
254+
if command is None:
255+
raise ValueError(
256+
"Command '{}' not found. Set the {} environment variable to "
257+
"override slapdtest's search path.".format(value, var_name)
258+
)
259+
return command
282260

283261
def setup_rundir(self):
284262
"""
@@ -439,7 +417,6 @@ def start(self):
439417
"""
440418

441419
if self._proc is None:
442-
self._check_requirements()
443420
# prepare directory structure
444421
atexit.register(self.stop)
445422
self._cleanup_rundir()

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy