diff --git a/.github/workflows/python.yaml b/.github/workflows/python.yaml index 2e92fa6..e00a87b 100644 --- a/.github/workflows/python.yaml +++ b/.github/workflows/python.yaml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.6, 3.7, 3.8, 3.9] + python-version: ["3.10", "3.11", "3.12", "3.13"] steps: - name: Install ldap dependencies run: sudo apt-get update && sudo apt-get install libldap2-dev libsasl2-dev diff --git a/.pylintrc b/.pylintrc index cf9b82b..b07461a 100644 --- a/.pylintrc +++ b/.pylintrc @@ -1,33 +1,29 @@ [MASTER] -ignore = ,input +ignore-paths=input persistent = yes [MESSAGES CONTROL] disable = missing-docstring, - fixme, - duplicate-code, - no-member, - parse-error, - bad-continuation, - too-few-public-methods, - global-statement, - cyclic-import, - locally-disabled, - file-ignored, + fixme, + duplicate-code, + no-member, + parse-error, + too-few-public-methods, + global-statement, + cyclic-import, + locally-disabled, + file-ignored, import-error, unused-import [REPORTS] output-format = text -files-output = no reports = no [FORMAT] max-line-length = 120 -max-statement-lines = 75 single-line-if-stmt = no -no-space-check = trailing-comma,dict-separator max-module-lines = 1000 indent-string = ' ' @@ -75,8 +71,8 @@ good-names=logger,id,ID # Bad variable names which should always be refused, separated by a comma bad-names=foo,bar,baz,toto,tutu,tata -# List of builtins function names that should not be used, separated by a comma -bad-functions=apply,input +# Note: bad-functions option was removed in modern pylint versions +# Use specific pylint checks instead [DESIGN] @@ -92,4 +88,4 @@ min-public-methods = 2 max-public-methods = 20 [EXCEPTIONS] -overgeneral-exceptions = Exception +overgeneral-exceptions = builtins.Exception diff --git a/csh_ldap/__init__.py b/csh_ldap/__init__.py index 80822bc..fc67cce 100644 --- a/csh_ldap/__init__.py +++ b/csh_ldap/__init__.py @@ -11,7 +11,7 @@ class CSHLDAP: __domain__ = "csh.rit.edu" @reconnect_on_fail - def __init__(self, bind_dn, bind_pw, batch_mods=False, + def __init__(self, bind_dn, bind_pw, *, batch_mods=False, sasl=False, ro=False): """Handler for bindings to CSH LDAP. @@ -74,7 +74,7 @@ def get_member_ibutton(self, val): members = self.__con__.search_s( CSHMember.__ldap_user_ou__, ldap.SCOPE_SUBTREE, - "(ibutton=%s)" % val, + f"(ibutton={val})", ['ipaUniqueID']) if members: return CSHMember( @@ -96,7 +96,7 @@ def get_member_slackuid(self, slack): members = self.__con__.search_s( CSHMember.__ldap_user_ou__, ldap.SCOPE_SUBTREE, - "(slackuid=%s)" % slack, + f"(slackuid={slack})", ['ipaUniqueID']) if members: return CSHMember( @@ -132,7 +132,7 @@ def get_directorship_heads(self, val): res = self.__con__.search_s( __ldap_group_ou__, ldap.SCOPE_SUBTREE, - "(cn=eboard-%s)" % val, + f"(cn=eboard-{val})", ['member']) ret = [] @@ -176,19 +176,14 @@ def flush_mod(self): mod_str = "ADD" else: mod_str = "REPLACE" - print("{} VALUE {} = {} FOR {}".format(mod_str, - mod[1], - mod[2], - dn)) + print(f"{mod_str} VALUE {mod[1]} = {mod[2]} FOR {dn}") else: self.__con__.modify_s(dn, self.__mod_queue__[dn]) except ldap.TYPE_OR_VALUE_EXISTS: - print("Error! Conflicting Batch Modification: %s" - % str(self.__mod_queue__[dn])) + print(f"Error! Conflicting Batch Modification: {self.__mod_queue__[dn]}") continue except ldap.NO_SUCH_ATTRIBUTE: - print("Error! Conflicting Batch Modification: %s" - % str(self.__mod_queue__[dn])) + print(f"Error! Conflicting Batch Modification: {self.__mod_queue__[dn]}") continue self.__mod_queue__[dn] = None self.__pending_mod_dn__ = [] diff --git a/csh_ldap/group.py b/csh_ldap/group.py index 667f960..8c9896a 100644 --- a/csh_ldap/group.py +++ b/csh_ldap/group.py @@ -21,7 +21,7 @@ def __init__(self, lib, search_val): res = self.__con__.search_s( self.__ldap_group_ou__, ldap.SCOPE_SUBTREE, - "(cn=%s)" % search_val, + f"(cn={search_val})", ['cn']) if res: @@ -35,7 +35,7 @@ def get_members(self): res = self.__con__.search_s( self.__ldap_base_dn__, ldap.SCOPE_SUBTREE, - "(memberof=%s)" % self.__dn__, + f"(memberof={self.__dn__})", ['uid']) ret = [] @@ -69,13 +69,13 @@ def check_member(self, member, dn=False): res = self.__con__.search_s( self.__dn__, ldap.SCOPE_BASE, - "(member=%s)" % dn, + f"(member={dn})", ['ipaUniqueID']) else: res = self.__con__.search_s( self.__dn__, ldap.SCOPE_BASE, - "(member=%s)" % member.get_dn(), + f"(member={member.get_dn()})", ['ipaUniqueID']) return len(res) > 0 @@ -105,7 +105,7 @@ def add_member(self, member, dn=False): mod_attrs = [mod] self.__con__.modify_s(self.__dn__, mod_attrs) else: - print("ADD VALUE member = {} FOR {}".format(mod[2], self.__dn__)) + print(f"ADD VALUE member = {mod[2]} FOR {self.__dn__}") @reconnect_on_fail def del_member(self, member, dn=False): @@ -133,5 +133,4 @@ def del_member(self, member, dn=False): mod_attrs = [mod] self.__con__.modify_s(self.__dn__, mod_attrs) else: - print("DELETE VALUE member = {} FOR {}".format(mod[2], - self.__dn__)) + print(f"DELETE VALUE member = {mod[2]} FOR {self.__dn__}") diff --git a/csh_ldap/member.py b/csh_ldap/member.py index 5b72368..d0cd89d 100644 --- a/csh_ldap/member.py +++ b/csh_ldap/member.py @@ -24,13 +24,13 @@ def __init__(self, lib, search_val, uid): res = self.__con__.search_s( self.__ldap_user_ou__, ldap.SCOPE_SUBTREE, - "(uid=%s)" % search_val, + f"(uid={search_val})", ['ipaUniqueID']) else: res = self.__con__.search_s( self.__ldap_user_ou__, ldap.SCOPE_SUBTREE, - "(ipaUniqueID=%s)" % search_val, + f"(ipaUniqueID={search_val})", ['uid']) if res: @@ -51,7 +51,7 @@ def __hash__(self): def __repr__(self): """Generate a str representation of the bound CSH LDAP member object. """ - return "CSH Member(dn: %s)" % self.__dn__ + return f"CSH Member(dn: {self.__dn__})" def get(self, key): """Get an attribute from the bound CSH LDAP member object. @@ -59,7 +59,7 @@ def get(self, key): Arguments: key -- the attribute to get the value of """ - return self.__getattr__(key, as_list=True) + return self._get_attr(key, as_list=True) @reconnect_on_fail def groups(self): @@ -91,7 +91,7 @@ def get_dn(self): return self.__dn__ @reconnect_on_fail - def __getattr__(self, key, as_list=False): + def _get_attr(self, key, as_list=False): res = self.__con__.search_s( self.__dn__, ldap.SCOPE_BASE, @@ -153,7 +153,4 @@ def __setattr__(self, key, value): mod_str = "ADD" else: mod_str = "REPLACE" - print("{} FIELD {} WITH {} FOR {}".format(mod_str, - key, - value, - self.__dn__)) + print(f"{mod_str} FIELD {key} WITH {value} FOR {self.__dn__}") diff --git a/csh_ldap/utility.py b/csh_ldap/utility.py index 5329dec..f7f88fb 100644 --- a/csh_ldap/utility.py +++ b/csh_ldap/utility.py @@ -25,12 +25,14 @@ def wrapper(*method_args, **method_kwargs): :return: result of method call """ max_reconnects = MAX_RECONNECTS - is_cshldap = lambda arg: any( + + def is_cshldap(arg): + return any( filter( lambda t: t.__name__ == 'CSHLDAP', type(arg).__mro__ - ) ) + ) ldap_obj = next(filter(is_cshldap, method_args)) \ if any(filter(is_cshldap, method_args)) \ else method_args[0].__lib__ @@ -55,5 +57,6 @@ def wrapper(*method_args, **method_kwargs): max_reconnects -= 1 if max_reconnects == 0: raise + return None return wrapper diff --git a/requirements-test.txt b/requirements-test.txt index a4a1dd1..c68bf86 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,2 +1,2 @@ pycodestyle==2.4.0 -pylint==2.5.2 +pylint==3.3.8