Hello community,

here is the log from the commit of package python-ldap3 for openSUSE:Factory 
checked in at 2019-05-16 22:07:40
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-ldap3 (Old)
 and      /work/SRC/openSUSE:Factory/.python-ldap3.new.5148 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-ldap3"

Thu May 16 22:07:40 2019 rev:8 rq:703011 version:2.6

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-ldap3/python-ldap3.changes        
2019-02-27 15:05:46.330453891 +0100
+++ /work/SRC/openSUSE:Factory/.python-ldap3.new.5148/python-ldap3.changes      
2019-05-16 22:07:43.938409006 +0200
@@ -1,0 +2,18 @@
+Tue May 14 21:44:09 UTC 2019 - Gary Smith <[email protected]>
+
+- update to version 2.6
+
+# 2.6 - 2019.03.24
+    - fixed empty digestMd5.py file in 2.5.2 package
+    - explicitly declare digest module md5 in util.ntlm (thanks adawalli)
+    - change object passed to modify() was unexpectedly mutated (thanks John)
+    - added LDAPInfoError exception
+    - added Server.has_control(control) method to check if a server has a 
specific control
+    - added Server.has_extension(extension) method to check if a server has a 
specific extension
+    - added Server.has_feature(feature) method to check if a server has a 
specific feature
+    - fixed checking of \\ in safe_dn (thanks Maxim)
+    - fixed uuid checking with 5c byte value
+    - added single=True parameter to the ServerPool object definition. Servers 
state is shared between connections using the same pool
+    - updated copyright notice
+
+-------------------------------------------------------------------

Old:
----
  v2.5.2.tar.gz

New:
----
  v2.6.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-ldap3.spec ++++++
--- /var/tmp/diff_new_pack.AoAM7e/_old  2019-05-16 22:07:45.710407253 +0200
+++ /var/tmp/diff_new_pack.AoAM7e/_new  2019-05-16 22:07:45.710407253 +0200
@@ -18,7 +18,7 @@
 
 %{?!python_module:%define python_module() python-%{**} python3-%{**}}
 Name:           python-ldap3
-Version:        2.5.2
+Version:        2.6
 Release:        0
 Summary:        A strictly RFC 4511 conforming LDAP V3 pure Python client
 License:        LGPL-3.0-only

++++++ v2.5.2.tar.gz -> v2.6.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ldap3-2.5.2/.gitignore new/ldap3-2.6/.gitignore
--- old/ldap3-2.5.2/.gitignore  2018-12-23 09:49:04.000000000 +0100
+++ new/ldap3-2.6/.gitignore    2019-03-23 16:43:44.000000000 +0100
@@ -63,4 +63,178 @@
 
 #PyCharm
 .idea/
-*.iml
\ No newline at end of file
+*.iml
+### JetBrains template
+# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, 
CLion, Android Studio and WebStorm
+# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
+
+# User-specific stuff
+.idea/**/workspace.xml
+.idea/**/tasks.xml
+.idea/**/usage.statistics.xml
+.idea/**/dictionaries
+.idea/**/shelf
+
+# Sensitive or high-churn files
+.idea/**/dataSources/
+.idea/**/dataSources.ids
+.idea/**/dataSources.local.xml
+.idea/**/sqlDataSources.xml
+.idea/**/dynamic.xml
+.idea/**/uiDesigner.xml
+.idea/**/dbnavigator.xml
+
+# Gradle
+.idea/**/gradle.xml
+.idea/**/libraries
+
+# Gradle and Maven with auto-import
+# When using Gradle or Maven with auto-import, you should exclude module files,
+# since they will be recreated, and may cause churn.  Uncomment if using
+# auto-import.
+# .idea/modules.xml
+# .idea/*.iml
+# .idea/modules
+
+# CMake
+cmake-build-*/
+
+# Mongo Explorer plugin
+.idea/**/mongoSettings.xml
+
+# File-based project format
+*.iws
+
+# IntelliJ
+out/
+
+# mpeltonen/sbt-idea plugin
+.idea_modules/
+
+# JIRA plugin
+atlassian-ide-plugin.xml
+
+# Cursive Clojure plugin
+.idea/replstate.xml
+
+# Crashlytics plugin (for Android Studio and IntelliJ)
+com_crashlytics_export_strings.xml
+crashlytics.properties
+crashlytics-build.properties
+fabric.properties
+
+# Editor-based Rest Client
+.idea/httpRequests
+### VirtualEnv template
+# Virtualenv
+# http://iamzed.com/2009/05/07/a-primer-on-virtualenv/
+[Bb]in
+[Ii]nclude
+[Ll]ib
+[Ll]ib64
+[Ll]ocal
+[Ss]cripts
+pyvenv.cfg
+.venv
+pip-selfcheck.json
+### Windows template
+# Windows thumbnail cache files
+Thumbs.db
+ehthumbs.db
+ehthumbs_vista.db
+
+# Dump file
+*.stackdump
+
+# Folder config file
+[Dd]esktop.ini
+
+# Recycle Bin used on file shares
+$RECYCLE.BIN/
+
+# Windows Installer files
+*.cab
+*.msi
+*.msix
+*.msm
+*.msp
+
+# Windows shortcuts
+*.lnk
+### Python template
+# Byte-compiled / optimized / DLL files
+
+# C extensions
+
+# Distribution / packaging
+wheels/
+MANIFEST
+
+# PyInstaller
+#  Usually these files are written by a python script from a template
+#  before PyInstaller builds the exe, so as to inject date/other infos into it.
+
+# Installer logs
+
+# Unit test / coverage reports
+*.cover
+.hypothesis/
+.pytest_cache/
+
+# Translations
+
+# Django stuff:
+local_settings.py
+db.sqlite3
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+
+# PyBuilder
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# pyenv
+.python-version
+
+# celery beat schedule file
+celerybeat-schedule
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.env
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
+### Backup template
+*.bak
+*.gho
+*.ori
+*.orig
+*.tmp
+docs/manual/source/_build/
+docs/manual/source/_static/
+docs/manual/source/_templates/
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ldap3-2.5.2/CHANGES.txt new/ldap3-2.6/CHANGES.txt
--- old/ldap3-2.5.2/CHANGES.txt 2018-12-23 09:49:04.000000000 +0100
+++ new/ldap3-2.6/CHANGES.txt   2019-03-23 16:43:44.000000000 +0100
@@ -1,4 +1,5 @@
-# 2.5.2 - not yet released
+# 2.5.2 - 2018.12.28
+    - when starting tls before binding the connection is automatically open
     - fixed changelog date (thanks Adam)
     - support for AD timedeltas (thanks mprahl)
     - fixed WhoAmI in mock strategies (thanks mprahl)
@@ -9,6 +10,9 @@
     - fixed NTLM bind (thanks ribx)
     - server state in ServerPool is now a namedtuple "ServerState" (thanks 
Krisztian)
     - fixed error when adding member to AD group with unsafe DN (thanks Maxim)
+    - properly restore lazy status in reusable strategy (thanks Krisztian)
+    - ServerState namedtuple converted to class in core/pooling (thanks 
Krisztian)
+    - empty schema doesn't raise exception in Abstraction Layer (thanks ghost)
 
 
 # 2.5.1 - 2018.08.01
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ldap3-2.5.2/_changelog.txt 
new/ldap3-2.6/_changelog.txt
--- old/ldap3-2.5.2/_changelog.txt      2018-12-23 09:49:04.000000000 +0100
+++ new/ldap3-2.6/_changelog.txt        2019-03-23 16:43:44.000000000 +0100
@@ -1,4 +1,16 @@
-# 2.5.2 - not yet released
+# 2.6 - not yet released
+    - fixed empty digestMd5.py file in 2.5.2 package
+    - explicitly declare digest module md5 in util.ntlm (thanks adawalli)
+    - change object passed to modify() was unexpectedly mutated (thanks John)
+    - added LDAPInfoError exception
+    - added Server.has_control(control) method to check if a server has a 
specific control
+    - added Server.has_extension(extension) method to check if a server has a 
specific extension
+    - added Server.has_feature(feature) method to check if a server has a 
specific feature
+    - fixed checking of \\ in safe_dn (thanks Maxim)
+    - fixed uuid checking with 5c byte value
+    - added single=True parameter to the ServerPool object definition. Now 
server states is shared between connections
+
+# 2.5.2 - 2018.12.28
     - when starting tls before binding the connection is automatically open
     - fixed changelog date (thanks Adam)
     - support for AD timedeltas (thanks mprahl)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ldap3-2.5.2/_version.json new/ldap3-2.6/_version.json
--- old/ldap3-2.5.2/_version.json       2018-12-23 09:49:04.000000000 +0100
+++ new/ldap3-2.6/_version.json 2019-03-23 16:43:44.000000000 +0100
@@ -6,6 +6,6 @@
     "url": "https://github.com/cannatag/ldap3";,
     "description": "A strictly RFC 4510 conforming LDAP V3 pure Python client 
library",
     "author": "Giovanni Cannata",
-    "version": "2.5.2",
+    "version": "2.6",
     "license": "LGPL v3"
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ldap3-2.5.2/docs/manual/source/searches.rst 
new/ldap3-2.6/docs/manual/source/searches.rst
--- old/ldap3-2.5.2/docs/manual/source/searches.rst     2018-12-23 
09:49:04.000000000 +0100
+++ new/ldap3-2.6/docs/manual/source/searches.rst       2019-03-23 
16:43:44.000000000 +0100
@@ -157,7 +157,7 @@
 
 The scope of the search specifies how broad the search context will be. The 
LDAP database is a hierarchical structure
 (similar to a traditional file system) with a root and with container and leaf 
objects. a container can be stored in other
-containers, but not in a leaf object. It must be _clear that containers and 
leafs structure has nothing to do with the group
+containers, but not in a leaf object. It must be clear that containers and 
leafs structure has nothing to do with the group
 and group membership objects. A group (groupOfNames) object is a leaf object 
with a member attribute that contains references
 to other objects.
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ldap3-2.5.2/docs/manual/source/server.rst 
new/ldap3-2.6/docs/manual/source/server.rst
--- old/ldap3-2.5.2/docs/manual/source/server.rst       2018-12-23 
09:49:04.000000000 +0100
+++ new/ldap3-2.6/docs/manual/source/server.rst 2019-03-23 16:43:44.000000000 
+0100
@@ -78,6 +78,7 @@
 LDAPServerPoolExhaustedError exception. With ``exhaust=True`` if a server is 
not active it will be removed by the pool, if you set it
 to a number this will be the number of seconds an unreachable server is 
considered offline. When this timout expires the server
 is reinserted in the pool and checked again for availability.
+The pool keeps a single state for all connections that use it. If you want a 
different state for each connection you must set ``single=False`` while 
defining the ServerPool.
 
 When all servers in a pool are not available the strategy will wait for the 
number of seconds specified in ``ldap.POOLING_LOOP_TIMEOUT``
 before starting a new cycle. This defaults to 10 seconds.
@@ -149,4 +150,3 @@
 and then you can use the server as usual. Hostname must resolve to a real 
server.
 
 
-Attribute missing
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ldap3-2.5.2/ldap3/core/connection.py 
new/ldap3-2.6/ldap3/core/connection.py
--- old/ldap3-2.5.2/ldap3/core/connection.py    2018-12-23 09:49:04.000000000 
+0100
+++ new/ldap3-2.6/ldap3/core/connection.py      2019-03-23 16:43:44.000000000 
+0100
@@ -1037,6 +1037,7 @@
                     log(ERROR, '%s for <%s>', self.last_error, self)
                 raise LDAPChangeError(self.last_error)
 
+            changelist = dict()
             for attribute_name in changes:
                 if self.server and self.server.schema and self.check_names:
                     if ';' in attribute_name:  # remove tags for checking
@@ -1054,7 +1055,7 @@
                             log(ERROR, '%s for <%s>', self.last_error, self)
                         raise LDAPChangeError(self.last_error)
 
-                    changes[attribute_name] = [change]  # insert change in a 
tuple
+                    changelist[attribute_name] = [change]  # insert change in 
a list
                 else:
                     for change_operation in change:
                         if len(change_operation) != 2 or change_operation[0] 
not in [MODIFY_ADD, MODIFY_DELETE, MODIFY_REPLACE, MODIFY_INCREMENT, 0, 1, 2, 
3]:
@@ -1062,7 +1063,8 @@
                             if log_enabled(ERROR):
                                 log(ERROR, '%s for <%s>', self.last_error, 
self)
                             raise LDAPChangeError(self.last_error)
-            request = modify_operation(dn, changes, self.auto_encode, 
self.server.schema if self.server else None, 
validator=self.server.custom_validator if self.server else None, 
check_names=self.check_names)
+                    changelist[attribute_name] = change
+            request = modify_operation(dn, changelist, self.auto_encode, 
self.server.schema if self.server else None, 
validator=self.server.custom_validator if self.server else None, 
check_names=self.check_names)
             if log_enabled(PROTOCOL):
                 log(PROTOCOL, 'MODIFY request <%s> sent via <%s>', 
modify_request_to_dict(request), self)
             response = 
self.post_send_single_response(self.send('modifyRequest', request, controls))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ldap3-2.5.2/ldap3/core/exceptions.py 
new/ldap3-2.6/ldap3/core/exceptions.py
--- old/ldap3-2.5.2/ldap3/core/exceptions.py    2018-12-23 09:49:04.000000000 
+0100
+++ new/ldap3-2.6/ldap3/core/exceptions.py      2019-03-23 16:43:44.000000000 
+0100
@@ -376,9 +376,11 @@
 class LDAPCursorError(LDAPExceptionError):
     pass
 
+
 class LDAPObjectDereferenceError(LDAPExceptionError):
     pass
 
+
 # security exceptions
 class LDAPSSLNotSupportedError(LDAPExceptionError, ImportError):
     pass
@@ -505,6 +507,10 @@
     pass
 
 
+class LDAPInfoError(LDAPExceptionError):
+    pass
+
+
 # communication exceptions
 class LDAPCommunicationError(LDAPExceptionError):
     pass
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ldap3-2.5.2/ldap3/core/pooling.py 
new/ldap3-2.6/ldap3/core/pooling.py
--- old/ldap3-2.5.2/ldap3/core/pooling.py       2018-12-23 09:49:04.000000000 
+0100
+++ new/ldap3-2.6/ldap3/core/pooling.py 2019-03-23 16:43:44.000000000 +0100
@@ -195,7 +195,8 @@
                  servers=None,
                  pool_strategy=ROUND_ROBIN,
                  active=True,
-                 exhaust=False):
+                 exhaust=False,
+                 single_state=True):
 
         if pool_strategy not in POOLING_STRATEGIES:
             if log_enabled(ERROR):
@@ -209,6 +210,8 @@
         self.pool_states = dict()
         self.active = active
         self.exhaust = exhaust
+        self.single = single_state
+        self._pool_state = None # used for storing the global state of the pool
         if isinstance(servers, SEQUENCE_TYPES + (Server, )):
             self.add(servers)
         elif isinstance(servers, STRING_TYPES):
@@ -276,9 +279,13 @@
                 log(ERROR, 'server must be a Server of a list of Servers when 
adding to Server Pool <%s>', self)
             raise LDAPServerPoolError('server must be a Server or a list of 
Server')
 
-        for connection in self.pool_states:
-            # notifies connections using this pool to refresh
-            self.pool_states[connection].refresh()
+        if self.single:
+            if self._pool_state:
+                self._pool_state.refresh()
+        else:
+            for connection in self.pool_states:
+                # notifies connections using this pool to refresh
+                self.pool_states[connection].refresh()
 
     def remove(self, server):
         if server in self.servers:
@@ -288,14 +295,22 @@
                 log(ERROR, 'server %s to be removed not in Server Pool <%s>', 
server, self)
             raise LDAPServerPoolError('server not in server pool')
 
-        for connection in self.pool_states:
-            # notifies connections using this pool to refresh
-            self.pool_states[connection].refresh()
+        if self.single:
+            if self._pool_state:
+                self._pool_state.refresh()
+        else:
+            for connection in self.pool_states:
+                # notifies connections using this pool to refresh
+                self.pool_states[connection].refresh()
 
     def initialize(self, connection):
-        pool_state = ServerPoolState(self)
         # registers pool_state in ServerPool object
-        self.pool_states[connection] = pool_state
+        if self.single:
+            if not self._pool_state:
+                self._pool_state = ServerPoolState(self)
+            self.pool_states[connection] = self._pool_state
+        else:
+            self.pool_states[connection] = ServerPoolState(self)
 
     def get_server(self, connection):
         if connection in self.pool_states:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ldap3-2.5.2/ldap3/core/server.py 
new/ldap3-2.6/ldap3/core/server.py
--- old/ldap3-2.5.2/ldap3/core/server.py        2018-12-23 09:49:04.000000000 
+0100
+++ new/ldap3-2.6/ldap3/core/server.py  2019-03-23 16:43:44.000000000 +0100
@@ -28,7 +28,7 @@
 from datetime import datetime, MINYEAR
 
 from .. import DSA, SCHEMA, ALL, BASE, get_config_parameter, 
OFFLINE_EDIR_8_8_8, OFFLINE_AD_2012_R2, OFFLINE_SLAPD_2_4, OFFLINE_DS389_1_3_3, 
SEQUENCE_TYPES, IP_SYSTEM_DEFAULT, IP_V4_ONLY, IP_V6_ONLY, IP_V4_PREFERRED, 
IP_V6_PREFERRED, STRING_TYPES
-from .exceptions import LDAPInvalidServerError, LDAPDefinitionError, 
LDAPInvalidPortError, LDAPInvalidTlsSpecificationError, LDAPSocketOpenError
+from .exceptions import LDAPInvalidServerError, LDAPDefinitionError, 
LDAPInvalidPortError, LDAPInvalidTlsSpecificationError, LDAPSocketOpenError, 
LDAPInfoError
 from ..protocol.formatters.standard import format_attribute_values
 from ..protocol.rfc4511 import LDAP_MAX_INT
 from ..protocol.rfc4512 import SchemaInfo, DsaInfo
@@ -69,7 +69,6 @@
     _message_counter = 0
     _message_id_lock = Lock()  # global lock for message_id shared by all 
Server objects
 
-
     def __init__(self,
                  host,
                  port=None,
@@ -570,3 +569,34 @@
                 for candidate in candidates:
                     log(BASIC, 'obtained candidate address for <%s>: <%r> with 
mode %s', self, candidate[:-2], self.mode)
         return candidates
+
+    def _check_info_property(self, kind, name):
+        if not self._dsa_info:
+            raise LDAPInfoError('server info not loaded')
+
+        if kind == 'control':
+            properties = self.info.supported_controls
+        elif kind == 'extension':
+            properties = self.info.supported_extensions
+        elif kind == 'feature':
+            properties = self.info.supported_features
+        else:
+            raise LDAPInfoError('invalid info category')
+
+        for prop in properties:
+                if name == prop[0] or (prop[2] and name.lower() == 
prop[2].lower()):  # checks oid and description
+                    return True
+
+        return False
+
+    def has_control(self, control):
+        return self._check_info_property('control', control)
+
+    def has_extension(self, extension):
+        return self._check_info_property('extension', extension)
+
+    def has_feature(self, feature):
+        return self._check_info_property('feature', feature)
+
+
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ldap3-2.5.2/ldap3/protocol/convert.py 
new/ldap3-2.6/ldap3/protocol/convert.py
--- old/ldap3-2.5.2/ldap3/protocol/convert.py   2018-12-23 09:49:04.000000000 
+0100
+++ new/ldap3-2.6/ldap3/protocol/convert.py     2019-03-23 16:43:44.000000000 
+0100
@@ -37,6 +37,7 @@
     except PyAsn1Error:  # invalid encoding, return bytes value
         return {'type': str(attribute['type']), 'values': [bytes(val) for val 
in attribute['vals']]}
 
+
 def attributes_to_dict(attributes):
     attributes_dict = dict()
     for attribute in attributes:
@@ -93,6 +94,7 @@
         except Exception:
             return {'attribute': str(ava['attributeDesc']), 'value': 
bytes(ava['assertionValue'])}
 
+
 def substring_to_dict(substring):
     return {'initial': substring['initial'] if substring['initial'] else '', 
'any': [middle for middle in substring['any']] if substring['any'] else '', 
'final': substring['final'] if substring['final'] else ''}
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ldap3-2.5.2/ldap3/protocol/formatters/formatters.py 
new/ldap3-2.6/ldap3/protocol/formatters/formatters.py
--- old/ldap3-2.5.2/ldap3/protocol/formatters/formatters.py     2018-12-23 
09:49:04.000000000 +0100
+++ new/ldap3-2.6/ldap3/protocol/formatters/formatters.py       2019-03-23 
16:43:44.000000000 +0100
@@ -32,6 +32,7 @@
 
 from ...core.timezone import OffsetTzInfo
 
+
 def format_unicode(raw_value):
     try:
         if str is not bytes:  # Python 3
@@ -108,16 +109,17 @@
     try:
         timestamp = int(raw_value)
         if timestamp < 0:  # ad timestamp cannot be negative
-            return raw_value
+            timestamp = timestamp * -1
     except Exception:
         return raw_value
 
     try:
-        return datetime.fromtimestamp(timestamp / 10000000.0 - 11644473600, 
tz=OffsetTzInfo(0, 'UTC'))  # forces true division in python 2
+        return datetime.fromtimestamp(timestamp / 10000000.0 - 11644473600,
+                                      tz=OffsetTzInfo(0, 'UTC'))  # forces 
true division in python 2
     except (OSError, OverflowError, ValueError):  # on Windows backwards 
timestamps are not allowed
         try:
             unix_epoch = datetime.fromtimestamp(0, tz=OffsetTzInfo(0, 'UTC'))
-            diff_seconds = timedelta(seconds=timestamp/10000000.0 - 
11644473600)
+            diff_seconds = timedelta(seconds=timestamp / 10000000.0 - 
11644473600)
             return unix_epoch + diff_seconds
         except Exception:
             pass
@@ -129,6 +131,7 @@
 
 try:  # uses regular expressions and the timezone class (python3.2 and later)
     from datetime import timezone
+
     time_format = re.compile(
         r'''
         ^
@@ -158,6 +161,7 @@
         re.VERBOSE
     )
 
+
     def format_time(raw_value):
         try:
             match = time_format.fullmatch(to_unicode(raw_value))
@@ -218,12 +222,12 @@
         representing a date and time. The LDAP-specific encoding of a value
         of this syntax is a restriction of the format defined in [ISO8601],
         and is described by the following ABNF:
-    
+
         GeneralizedTime = century year month day hour
                            [ minute [ second / leap-second ] ]
                            [ fraction ]
                            g-time-zone
-    
+
         century = 2(%x30-39) ; "00" to "99"
         year    = 2(%x30-39) ; "00" to "99"
         month   =   ( %x30 %x31-39 ) ; "01" (January) to "09"
@@ -242,7 +246,9 @@
             MINUS           = %x2D  ; minus sign ("-")
         """
 
-        if len(raw_value) < 10 or not all((c in b'0123456789+-,.Z' for c in 
raw_value)) or (b'Z' in raw_value and not raw_value.endswith(b'Z')):  # first 
ten characters are mandatory and must be numeric or timezone or fraction
+        if len(raw_value) < 10 or not all((c in b'0123456789+-,.Z' for c in 
raw_value)) or (
+                b'Z' in raw_value and not raw_value.endswith(
+                b'Z')):  # first ten characters are mandatory and must be 
numeric or timezone or fraction
             return raw_value
 
         # sets position for fixed values
@@ -305,9 +311,11 @@
                 return raw_value
 
             if str is not bytes:  # Python 3
-                timezone = OffsetTzInfo((timezone_hour * 60 + timezone_minute) 
* (1 if sep == b'+' else -1), 'UTC' + str(sep + offset, encoding='utf-8'))
+                timezone = OffsetTzInfo((timezone_hour * 60 + timezone_minute) 
* (1 if sep == b'+' else -1),
+                                        'UTC' + str(sep + offset, 
encoding='utf-8'))
             else:  # Python 2
-                timezone = OffsetTzInfo((timezone_hour * 60 + timezone_minute) 
* (1 if sep == b'+' else -1), unicode('UTC' + sep + offset, encoding='utf-8'))
+                timezone = OffsetTzInfo((timezone_hour * 60 + timezone_minute) 
* (1 if sep == b'+' else -1),
+                                        unicode('UTC' + sep + offset, 
encoding='utf-8'))
 
         try:
             return datetime(year=year,
@@ -334,7 +342,7 @@
     # positive, we can reuse format_ad_timestamp to get a datetime object.
     # Afterwards, we can subtract a datetime representing 0 hour on January 1,
     # 1601 from the returned datetime to get the timedelta.
-    return format_ad_timestamp(raw_value * -1) - format_ad_timestamp(0)
+    return format_ad_timestamp(raw_value) - format_ad_timestamp(0)
 
 
 def format_time_with_0_year(raw_value):
@@ -399,7 +407,8 @@
             sub_authority = ''
             i = 0
             while i < sub_authority_count:
-                sub_authority += '-' + str(int.from_bytes(raw_value[8 + (i * 
4): 12 + (i * 4)], byteorder='little'))  # little endian
+                sub_authority += '-' + str(
+                    int.from_bytes(raw_value[8 + (i * 4): 12 + (i * 4)], 
byteorder='little'))  # little endian
                 i += 1
         else:  # Python 2
             revision = int(ord(raw_value[0]))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ldap3-2.5.2/ldap3/protocol/formatters/standard.py 
new/ldap3-2.6/ldap3/protocol/formatters/standard.py
--- old/ldap3-2.5.2/ldap3/protocol/formatters/standard.py       2018-12-23 
09:49:04.000000000 +0100
+++ new/ldap3-2.6/ldap3/protocol/formatters/standard.py 2019-03-23 
16:43:44.000000000 +0100
@@ -124,6 +124,7 @@
     '1.2.840.113556.1.4.51': (format_ad_timestamp, validate_ad_timestamp),  # 
lastLogoff (Microsoft)
     '1.2.840.113556.1.4.52': (format_ad_timestamp, validate_ad_timestamp),  # 
lastLogon (Microsoft)
     '1.2.840.113556.1.4.60': (format_ad_timedelta, validate_ad_timedelta),  # 
lockoutDuration (Microsoft)
+    '1.2.840.113556.1.4.61': (format_ad_timedelta, validate_ad_timedelta),  # 
lockOutObservationWindow (Microsoft)
     '1.2.840.113556.1.4.74': (format_ad_timedelta, validate_ad_timedelta),  # 
maxPwdAge (Microsoft)
     '1.2.840.113556.1.4.78': (format_ad_timedelta, validate_ad_timedelta),  # 
minPwdAge (Microsoft)
     '1.2.840.113556.1.4.96': (format_ad_timestamp, 
validate_zero_and_minus_one_and_positive_int),  # pwdLastSet (Microsoft, can be 
set to -1 only)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ldap3-2.5.2/ldap3/protocol/formatters/validators.py 
new/ldap3-2.6/ldap3/protocol/formatters/validators.py
--- old/ldap3-2.5.2/ldap3/protocol/formatters/validators.py     2018-12-23 
09:49:04.000000000 +0100
+++ new/ldap3-2.6/ldap3/protocol/formatters/validators.py       2019-03-23 
16:43:44.000000000 +0100
@@ -31,7 +31,7 @@
 
 from ... import SEQUENCE_TYPES, STRING_TYPES, NUMERIC_TYPES, INTEGER_TYPES
 from .formatters import format_time, format_ad_timestamp
-from ...utils.conv import to_raw, to_unicode, ldap_escape_to_bytes
+from ...utils.conv import to_raw, to_unicode, ldap_escape_to_bytes, 
escape_bytes
 
 # Validators return True if value is valid, False if value is not valid,
 # or a value different from True and False that is a valid value to substitute 
to the input value
@@ -97,7 +97,7 @@
     valid_values = []  # builds a list of valid int values
     from decimal import Decimal, InvalidOperation
     for element in input_value:
-        try:  # try to convert any type to int, an invalid conversion raise 
TypeError or ValueError, doublecheck with Decimal type, if both are valid and 
equal then then int() value is used
+        try:  #try to convert any type to int, an invalid conversion raise 
TypeError or ValueError, doublecheck with Decimal type, if both are valid and 
equal then then int() value is used
             value = to_unicode(element) if isinstance(element, bytes) else 
element
             decimal_value = Decimal(value)
             int_value = int(value)
@@ -258,6 +258,7 @@
     else:
         return True
 
+
 def validate_ad_timedelta(input_value):
     """
     Should be validated like an AD timestamp except that since it is a time
@@ -267,6 +268,7 @@
         return False
     return validate_ad_timestamp(input_value * -1)
 
+
 def validate_guid(input_value):
     """
     object guid in uuid format (Novell eDirectory)
@@ -309,6 +311,7 @@
     else:
         return True
 
+
 def validate_uuid(input_value):
     """
     object entryUUID in uuid format
@@ -386,7 +389,9 @@
                     error = True
             elif '\\' in element:
                 try:
-                    
valid_values.append(UUID(bytes_le=ldap_escape_to_bytes(element)).bytes_le)  # 
byte representation, value in little endian
+                    uuid = 
UUID(bytes_le=ldap_escape_to_bytes(element)).bytes_le
+                    uuid = escape_bytes(uuid)
+                    valid_values.append(uuid)  # byte representation, value in 
little endian
                     changed = True
                 except ValueError:
                     error = True
@@ -398,7 +403,7 @@
                     error = True
             if error and str == bytes:  # python2 only assume value is bytes 
and valid
                 valid_values.append(element)  # value is untouched, must be in 
little endian
-        elif isinstance(element, (bytes, bytearray)) :  # assumes bytes are 
valid uuid
+        elif isinstance(element, (bytes, bytearray)):  # assumes bytes are 
valid uuid
             valid_values.append(element)  # value is untouched, must be in 
little endian
         else:
             return False
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ldap3-2.5.2/ldap3/protocol/sasl/digestMd5.py 
new/ldap3-2.6/ldap3/protocol/sasl/digestMd5.py
--- old/ldap3-2.5.2/ldap3/protocol/sasl/digestMd5.py    2018-12-23 
09:49:04.000000000 +0100
+++ new/ldap3-2.6/ldap3/protocol/sasl/digestMd5.py      2019-03-23 
16:43:44.000000000 +0100
@@ -66,7 +66,7 @@
     if not isinstance(s, bytes):
         s = s.encode()
 
-    return hmac.new(k, s).hexdigest()
+    return hmac.new(k, s, digestmod=hashlib.md5).hexdigest()
 
 
 def sasl_digest_md5(connection, controls):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ldap3-2.5.2/ldap3/utils/conv.py 
new/ldap3-2.6/ldap3/utils/conv.py
--- old/ldap3-2.5.2/ldap3/utils/conv.py 2018-12-23 09:49:04.000000000 +0100
+++ new/ldap3-2.6/ldap3/utils/conv.py   2019-03-23 16:43:44.000000000 +0100
@@ -146,6 +146,7 @@
     else:  # Python 2
         return value.decode()
 
+
 def json_encode_b64(obj):
     try:
         return dict(encoding='base64', encoded=b64encode(obj))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ldap3-2.5.2/ldap3/utils/dn.py 
new/ldap3-2.6/ldap3/utils/dn.py
--- old/ldap3-2.5.2/ldap3/utils/dn.py   2018-12-23 09:49:04.000000000 +0100
+++ new/ldap3-2.6/ldap3/utils/dn.py     2019-03-23 16:43:44.000000000 +0100
@@ -319,7 +319,7 @@
 
     if dn.startswith('<GUID=') and dn.endswith('>'):  # Active Directory 
allows looking up objects by putting its GUID in a specially-formatted DN (e.g. 
'<GUID=7b95f0d5-a3ed-486c-919c-077b8c9731f2>')
         escaped_dn = dn
-    elif '@' not in dn and '\\' not in dn:  # active directory UPN (User 
Principal Name) consist of an account, the at sign (@) and a domain, or the 
domain level logn name domain\username
+    elif '@' not in dn:  # active directory UPN (User Principal Name) consist 
of an account, the at sign (@) and a domain, or the domain level logn name 
domain\username
         for component in parse_dn(dn, escape=True):
             if decompose:
                 escaped_dn.append((component[0], component[1], component[2]))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ldap3-2.5.2/ldap3/utils/ntlm.py 
new/ldap3-2.6/ldap3/utils/ntlm.py
--- old/ldap3-2.5.2/ldap3/utils/ntlm.py 2018-12-23 09:49:04.000000000 +0100
+++ new/ldap3-2.6/ldap3/utils/ntlm.py   2019-03-23 16:43:44.000000000 +0100
@@ -483,7 +483,7 @@
         temp += self.server_target_info_raw
         temp += pack('<I', 0)  # Z(4)
         response_key_nt = self.ntowf_v2()
-        nt_proof_str = hmac.new(response_key_nt, self.server_challenge + 
temp).digest()
+        nt_proof_str = hmac.new(response_key_nt, self.server_challenge + temp, 
digestmod=hashlib.md5).digest()
         nt_challenge_response = nt_proof_str + temp
         return nt_challenge_response
 
@@ -494,4 +494,4 @@
             password_digest = binascii.unhexlify(passparts[1])
         else:
             password_digest = hashlib.new('MD4', 
self._password.encode('utf-16-le')).digest()
-        return hmac.new(password_digest, (self.user_name.upper() + 
self.user_domain).encode('utf-16-le')).digest()
+        return hmac.new(password_digest, (self.user_name.upper() + 
self.user_domain).encode('utf-16-le'), digestmod=hashlib.md5).digest()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ldap3-2.5.2/ldap3/version.py 
new/ldap3-2.6/ldap3/version.py
--- old/ldap3-2.5.2/ldap3/version.py    2018-12-23 09:49:04.000000000 +0100
+++ new/ldap3-2.6/ldap3/version.py      2019-03-23 16:43:44.000000000 +0100
@@ -1,6 +1,6 @@
 # THIS FILE IS AUTO-GENERATED. PLEASE DO NOT MODIFY# version file for ldap3
-# generated on 2018-12-07 23:55:26.075512
-# on system uname_result(system='Windows', node='ELITE10GC', release='10', 
version='10.0.17134', machine='AMD64', processor='Intel64 Family 6 Model 58 
Stepping 9, GenuineIntel')
+# generated on 2018-12-28 22:18:22.295577
+# on system uname_result(system='Windows', node='ELITE10GC', release='10', 
version='10.0.17763', machine='AMD64', processor='Intel64 Family 6 Model 58 
Stepping 9, GenuineIntel')
 # with Python 3.7.1 - ('v3.7.1:260ec2c36a', 'Oct 20 2018 14:57:15') - MSC 
v.1915 64 bit (AMD64)
 #
 __version__ = '2.5.2'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ldap3-2.5.2/test/config.py 
new/ldap3-2.6/test/config.py
--- old/ldap3-2.5.2/test/config.py      2018-12-23 09:49:04.000000000 +0100
+++ new/ldap3-2.6/test/config.py        2019-03-23 16:43:44.000000000 +0100
@@ -38,7 +38,7 @@
 from ldap3 import __version__ as ldap3_version
 from pyasn1 import __version__ as pyasn1_version
 
-test_strategy = SYNC  # possible choices: SYNC, ASYNC, RESTARTABLE, REUSABLE, 
MOCK_SYNC (not used on TRAVIS - look at .travis.yml)
+test_strategy = SYNC  # possible choices: SYNC, ASYNC, RESTARTABLE, REUSABLE, 
MOCK_SYNC, MOCK_ASYNC (not used on TRAVIS - look at .travis.yml)
 test_server_type = 'EDIR'  # possible choices: EDIR (Novell eDirectory), AD 
(Microsoft Active Directory), SLAPD (OpenLDAP)
 
 test_pool_size = 5
@@ -58,7 +58,7 @@
 test_receive_timeout = None
 test_auto_escape = True
 test_auto_encode = True
-test_lazy_connection = False
+test_lazy_connection = True
 test_user_password = 'Rc2597pfop'  # default password for users created in 
tests
 
 test_validator = {}
@@ -182,7 +182,7 @@
     # test notepbook - eDirectory (EDIR)
     # test_server = ['edir1.hyperv',
     #                'edir2.hyperv']  # ldap server where tests are executed, 
if a list is given a pool will be created
-    test_server = 'edir1.hyperv'
+    test_server = 'edir4.hyperv'
     test_server_type = 'EDIR'
     test_root_partition = ''
     test_base = 'ou=fixtures,o=test'  # base context where test objects are 
created
@@ -192,7 +192,7 @@
     test_multivalued_attribute = 'givenname'
     test_singlevalued_attribute = 'generationQualifier'
     test_server_context = 'o=resources'  # used in novell eDirectory extended 
operations
-    test_server_edir_name = 'edir1'  # used in novell eDirectory extended 
operations
+    test_server_edir_name = 'edir4'  # used in novell eDirectory extended 
operations
     test_user = 'cn=test_admin_user,ou=bind,o=test'  # the user that performs 
the tests
     test_password = 'password1'  # user password
     test_secondary_user = 'cn=test_bind_user,ou=bind,o=test'
@@ -365,6 +365,7 @@
 print('Logging:', 'False' if not test_logging else test_logging_filename, '- 
Log detail:', (get_detail_level_name(test_log_detail) if test_logging else 
'None') + ' - Internal decoder: ', test_internal_decoder, ' - Response waiting 
timeout:', get_config_parameter('RESPONSE_WAITING_TIMEOUT'))
 print()
 
+
 def random_id():
     return str(SystemRandom().random())[-5:]
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ldap3-2.5.2/test/testFormatters.py 
new/ldap3-2.6/test/testFormatters.py
--- old/ldap3-2.5.2/test/testFormatters.py      2018-12-23 09:49:04.000000000 
+0100
+++ new/ldap3-2.6/test/testFormatters.py        1970-01-01 01:00:00.000000000 
+0100
@@ -1,12 +0,0 @@
-import unittest
-from datetime import timedelta
-
-from ldap3.protocol.formatters.formatters import format_ad_timedelta
-
-
-class TestFormatters(unittest.TestCase):
-    def test_format_ad_timedelta_thirty_mins(self):
-        self.assertEqual(format_ad_timedelta(-18000000000), 
timedelta(minutes=30))
-
-    def test_format_ad_timedelta_one_day(self):
-        self.assertEqual(format_ad_timedelta(-864000000000), timedelta(days=1))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ldap3-2.5.2/test/testValidators.py 
new/ldap3-2.6/test/testValidators.py
--- old/ldap3-2.5.2/test/testValidators.py      2018-12-23 09:49:04.000000000 
+0100
+++ new/ldap3-2.6/test/testValidators.py        2019-03-23 16:43:44.000000000 
+0100
@@ -1,7 +1,7 @@
 import unittest
 from datetime import datetime
 
-from ldap3.protocol.formatters.validators import validate_integer, 
validate_boolean, validate_bytes, validate_generic_single_value, validate_time, 
validate_zero_and_minus_one_and_positive_int, validate_ad_timedelta
+from ldap3.protocol.formatters.validators import validate_integer, 
validate_boolean, validate_bytes, validate_generic_single_value, validate_time, 
validate_zero_and_minus_one_and_positive_int
 from ldap3.core.timezone import OffsetTzInfo
 
 class Test(unittest.TestCase):
@@ -202,9 +202,3 @@
         self.assertTrue(validated)
         validated = validate_zero_and_minus_one_and_positive_int('-2')
         self.assertFalse(validated)
-
-    def test_validate_ad_timedelta_valid(self):
-        self.assertTrue(validate_ad_timedelta(-36288000000000))
-
-    def test_validate_ad_timedelta_invalid(self):
-        self.assertFalse(validate_ad_timedelta(123))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ldap3-2.5.2/upload-to-pypi.cmd 
new/ldap3-2.6/upload-to-pypi.cmd
--- old/ldap3-2.5.2/upload-to-pypi.cmd  2018-12-23 09:49:04.000000000 +0100
+++ new/ldap3-2.6/upload-to-pypi.cmd    2019-03-23 16:43:44.000000000 +0100
@@ -1 +1 @@
-\Python\Python36\Scripts\twine upload dist/*
+\Python\Python37\Scripts\twine upload dist/*


Reply via email to