From 587460a228441dfcc56cba9ce8bebd393c9f24bb Mon Sep 17 00:00:00 2001 From: pyldap contributors Date: Thu, 23 Nov 2017 21:42:00 +0100 Subject: [PATCH] py3: Add and use the ldap.compat module Add a module providing common things that differ between Python 2 and 3. (This is really a limited approximation of the six library) --- Lib/ldap/cidict.py | 2 +- Lib/ldap/compat.py | 43 +++++++++++++++++++++++++++++++++++++ Lib/ldap/ldapobject.py | 6 +++++- Lib/ldap/schema/models.py | 9 +++++--- Lib/ldap/schema/subentry.py | 5 +++-- Lib/ldapurl.py | 8 +++---- Lib/ldif.py | 8 +++---- Lib/slapdtest.py | 5 +++-- Tests/t_ldapurl.py | 8 +++---- setup.py | 1 + 10 files changed, 73 insertions(+), 22 deletions(-) create mode 100644 Lib/ldap/compat.py diff --git a/Lib/ldap/cidict.py b/Lib/ldap/cidict.py index 07832ef..fdfba4b 100644 --- a/Lib/ldap/cidict.py +++ b/Lib/ldap/cidict.py @@ -8,7 +8,7 @@ from ldap import __version__ -from UserDict import IterableUserDict +from ldap.compat import IterableUserDict class cidict(IterableUserDict): diff --git a/Lib/ldap/compat.py b/Lib/ldap/compat.py new file mode 100644 index 0000000..de0e110 --- /dev/null +++ b/Lib/ldap/compat.py @@ -0,0 +1,43 @@ +"""Compatibility wrappers for Py2/Py3.""" + +import sys + +if sys.version_info[0] < 3: + from UserDict import UserDict, IterableUserDict + from urllib import quote + from urllib import quote_plus + from urllib import unquote as urllib_unquote + from urllib import urlopen + from urlparse import urlparse + + def unquote(uri): + """Specialized unquote that uses UTF-8 for parsing.""" + uri = uri.encode('ascii') + unquoted = urllib_unquote(uri) + return unquoted.decode('utf-8') + + # Old-style of re-raising an exception is SyntaxError in Python 3, + # so hide behind exec() so the Python 3 parser doesn't see it + exec('''def reraise(exc_type, exc_value, exc_traceback): + """Re-raise an exception given information from sys.exc_info() + + Note that unlike six.reraise, this does not support replacing the + traceback. All arguments must come from a single sys.exc_info() call. + """ + raise exc_type, exc_value, exc_traceback + ''') + +else: + from collections import UserDict + IterableUserDict = UserDict + from urllib.parse import quote, quote_plus, unquote, urlparse + from urllib.request import urlopen + + def reraise(exc_type, exc_value, exc_traceback): + """Re-raise an exception given information from sys.exc_info() + + Note that unlike six.reraise, this does not support replacing the + traceback. All arguments must come from a single sys.exc_info() call. + """ + # In Python 3, all exception info is contained in one object. + raise exc_value diff --git a/Lib/ldap/ldapobject.py b/Lib/ldap/ldapobject.py index a199462..970f014 100644 --- a/Lib/ldap/ldapobject.py +++ b/Lib/ldap/ldapobject.py @@ -24,6 +24,7 @@ from ldap.schema import SCHEMA_ATTRS from ldap.controls import LDAPControl,DecodeControlTuples,RequestControlTuples from ldap.extop import ExtendedRequest,ExtendedResponse +from ldap.compat import reraise from ldap import LDAPError @@ -105,7 +106,10 @@ def _ldap_call(self,func,*args,**kwargs): pass if __debug__ and self._trace_level>=2: self._trace_file.write('=> LDAPError - %s: %s\n' % (e.__class__.__name__,str(e))) - raise exc_type,exc_value,exc_traceback + try: + reraise(exc_type, exc_value, exc_traceback) + finally: + exc_type = exc_value = exc_traceback = None else: if __debug__ and self._trace_level>=2: if not diagnostic_message_success is None: diff --git a/Lib/ldap/schema/models.py b/Lib/ldap/schema/models.py index 8471954..c3bb702 100644 --- a/Lib/ldap/schema/models.py +++ b/Lib/ldap/schema/models.py @@ -4,7 +4,10 @@ See https://www.python-ldap.org/ for details. """ -import UserDict,ldap.cidict +import sys + +import ldap.cidict +from ldap.compat import IterableUserDict from ldap.schema.tokenizer import split_tokens,extract_tokens @@ -615,7 +618,7 @@ def __str__(self): return '( %s )' % ''.join(result) -class Entry(UserDict.IterableUserDict): +class Entry(IterableUserDict): """ Schema-aware implementation of an LDAP entry class. @@ -628,7 +631,7 @@ def __init__(self,schema,dn,entry): self._attrtype2keytuple = {} self._s = schema self.dn = dn - UserDict.UserDict.__init__(self,{}) + IterableUserDict.IterableUserDict.__init__(self,{}) self.update(entry) def _at2key(self,nameoroid): diff --git a/Lib/ldap/schema/subentry.py b/Lib/ldap/schema/subentry.py index fbc33c5..4d42b19 100644 --- a/Lib/ldap/schema/subentry.py +++ b/Lib/ldap/schema/subentry.py @@ -473,8 +473,9 @@ def urlfetch(uri,trace_level=0): l.unbind_s() del l else: - import urllib,ldif - ldif_file = urllib.urlopen(uri) + import ldif + from ldap.compat import urlopen + ldif_file = urlopen(uri) ldif_parser = ldif.LDIFRecordList(ldif_file,max_entries=1) ldif_parser.parse() subschemasubentry_dn,s_temp = ldif_parser.all_records[0] diff --git a/Lib/ldapurl.py b/Lib/ldapurl.py index bbd6929..366e177 100644 --- a/Lib/ldapurl.py +++ b/Lib/ldapurl.py @@ -16,9 +16,7 @@ 'LDAPUrlExtension','LDAPUrlExtensions','LDAPUrl' ] -import UserDict - -from urllib import quote,unquote +from ldap.compat import UserDict, quote, unquote LDAP_SCOPE_BASE = 0 LDAP_SCOPE_ONELEVEL = 1 @@ -132,14 +130,14 @@ def __ne__(self,other): return not self.__eq__(other) -class LDAPUrlExtensions(UserDict.UserDict): +class LDAPUrlExtensions(UserDict): """ Models a collection of LDAP URL extensions as dictionary type """ def __init__(self,default=None): - UserDict.UserDict.__init__(self) + UserDict.__init__(self) for k,v in (default or {}).items(): self[k]=v diff --git a/Lib/ldif.py b/Lib/ldif.py index 318f46c..7659358 100644 --- a/Lib/ldif.py +++ b/Lib/ldif.py @@ -18,8 +18,6 @@ 'LDIFCopy', ] -import urlparse -import urllib import re from base64 import b64encode, b64decode @@ -28,6 +26,8 @@ except ImportError: from StringIO import StringIO +from ldap.compat import urlparse, urlopen + attrtype_pattern = r'[\w;.-]+(;[\w_-]+)*' attrvalue_pattern = r'(([^,]|\\,)+|".*?")' attrtypeandvalue_pattern = attrtype_pattern + r'[ ]*=[ ]*' + attrvalue_pattern @@ -350,9 +350,9 @@ def _next_key_and_value(self): url = unfolded_line[colon_pos+2:].strip() attr_value = None if self._process_url_schemes: - u = urlparse.urlparse(url) + u = urlparse(url) if u[0] in self._process_url_schemes: - attr_value = urllib.urlopen(url).read() + attr_value = urlopen(url).read() else: attr_value = unfolded_line[colon_pos+1:] return attr_type,attr_value diff --git a/Lib/slapdtest.py b/Lib/slapdtest.py index 2f98672..6bb43f0 100644 --- a/Lib/slapdtest.py +++ b/Lib/slapdtest.py @@ -14,7 +14,8 @@ import logging from logging.handlers import SysLogHandler import unittest -import urllib + +from ldap.compat import quote_plus # a template string for generating simple slapd.conf file SLAPD_CONF_TEMPLATE = r""" @@ -125,7 +126,7 @@ def __init__(self): self._db_directory = os.path.join(self.testrundir, "openldap-data") self.ldap_uri = "ldap://%s:%d/" % (LOCALHOST, self._port) ldapi_path = os.path.join(self.testrundir, 'ldapi') - self.ldapi_uri = "ldapi://%s" % urllib.quote_plus(ldapi_path) + self.ldapi_uri = "ldapi://%s" % quote_plus(ldapi_path) def setup_rundir(self): """ diff --git a/Tests/t_ldapurl.py b/Tests/t_ldapurl.py index c87b752..407538f 100644 --- a/Tests/t_ldapurl.py +++ b/Tests/t_ldapurl.py @@ -6,7 +6,7 @@ """ import unittest -import urllib +from ldap.compat import quote import ldapurl from ldapurl import LDAPUrl @@ -185,9 +185,9 @@ def test_combo(self): "ldap://127.0.0.1:1234/dc=example,dc=com" + "?attr1,attr2,attr3" + "?sub" - + "?" + urllib.quote("(objectClass=*)") - + "?bindname=" + urllib.quote("cn=d,c=au") - + ",X-BINDPW=" + urllib.quote("???") + + "?" + quote("(objectClass=*)") + + "?bindname=" + quote("cn=d,c=au") + + ",X-BINDPW=" + quote("???") + ",trace=8" ) self.assertEqual(u.urlscheme, "ldap") diff --git a/setup.py b/setup.py index 6bbf595..a475a35 100644 --- a/setup.py +++ b/setup.py @@ -137,6 +137,7 @@ class OpenLDAP2: 'ldap', 'slapdtest', 'ldap.async', + 'ldap.compat', 'ldap.controls', 'ldap.controls.deref', 'ldap.controls.libldap',