-
Notifications
You must be signed in to change notification settings - Fork 125
[python-ldap] Add type hints #522
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
With regard to the CI failures, I should add that With this patch applied:
|
Hi, thanks for the PR! Looks good, would you be able to split it into a few more commits each doing self-contained changes: any fixes, And just out of curiosity, is there a reason you chose to use |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Excellent work! A few comments and suggestions to push your PR over the finish line:
- Fix Python 3.6 compatibility issues. We still support old Python versions for $REASONS..., sorry.
- Please don't introduce yet another top-level module name. We like to move
ldapurl
andldif
into theldap
package eventually. I suggestldap.types
module. - Don't use
from ldap_types import *
. Star-imports are frowned upon and make code harder to read. - The PR is missing a PEP 561 type marker file. Add a
Lib/ldap/py.typed
file and add the file toMANIFEST.in
- Move
mypy.ini
content to eitherpyproject.toml
orsetup.cfg
- Extend
tox.ini
with a mypy check for every supported Python version - Add
_ldap.pyi
toMANIFEST.in
- Would it make sense to move
_ldap.pyi
toLib/_ldap.pyi
and install it along the shared extension?
Python 3.6 fails because it does not support future annotations:
File "/home/runner/work/python-ldap/python-ldap/.tox/c90-py36/lib/python3.6/site-packages/ldap/__init__.py", line 6
from __future__ import annotations
^
SyntaxError: future feature annotations is not defined
Hi! Why has this this work stalled? @Alphix are you still interested in continuing this?
What are those reasons? Many projects have already dropped 3.7. |
@intgr - it's the usual reason, I need to find the time to take the comments into account and update the patch. Maybe next week, we'll see...
My problem is that |
Because we (downstream vendor) still support Python 3.6. |
My question was: why can't python-ldap drop support for Python 3.6 (and maybe 3.7)? |
My guess would be RHEL? Anyway, does |
|
41ffdf3
to
8b4736f
Compare
That was an oversight, didn't realise it was relevant for backwards compatibility, I've fixed that in the second version. |
@tiran : I think I've addressed all the points you raised (and the CI seems happy). The only thing I can think of is that Also, I should add that this is just a starting point for type annotations. There are still a bunch of uses of |
This is in preparation for later patches which add typing.
Scratch that, I am working on splitting the PR into several preparatory patches, one big type hints patch, and one final patch integrating type checking (and learning a lot about
|
This is also in preparation for the typing patches as it helps to avoid import loops.
Redefining variables with varying types makes mypy unhappy.
The WrongResultType will receive an Interable (set) of integers representing the expected result types, doing a str.join() on that set doesn't work. Also, make sure that startSearch() has been called before processResults() is called which, in turn, ensures that self._msgId is not None.
First, __contains__() can be called with anything as the key, but only supports str keys (due to the key.lower() call), so make sure that the key is actually a str. Second, strlist_union() is described as returning a list, but actually returns an iterable, so make it return a list. @@ -134,4 +136,4 @@ def strlist_union(a,b):
Make sure that c_template is defined in the base class (it is already defined in all subclasses) so that it is always present. Also, fix a spelling mistake.
First, change class PersistentSearchControl(RequestControl) so that self.changeTypes is always a List[str] instead of List[str|int]. Second, in class EntryChangeNotificationControl, change decodeControlValue() to not return a value (see the ResponseControl parent class).
decodeControlValue() expects a binary encodedControlValue, so correct the comparison to reflect that.
The comments for the ProxyAuthzControl and AuthorizationIdentityResponseControl classes indicate that they expect/return a str (which makes sense), but then fail to actually deal with the str properly. The GetEffectiveRightsControl class has a similar issue (but seems to be a stub at the moment, the "authzId" parameter should probably be renamed "gerSubject" and can take much more than just a DN).
Avoid redefining a variable to a different type.
This helps to avoid import loops in later patches.
This helps with the type checking later.
Make class ResultProcessor an explicit subclass of LDAPObject This keeps type checkers happy but shouldn't have any functional difference.
Avoid redefining variables to keep type checkers happy.
First, the criticality in class SyncRequestControl can be passed as an int/bool/etc, but it still makes sense (and helps type checkers) to make sure that it is actually stored as a bool. Second, some minor code changes to exclude the possibility that some variables are not None and to help type checkers understand the type of some objects. Last, make sure that the syncrepl_get_cookie() function is type-conformant.
__eq__ methods need to be able to handle being passed any kind of object. The remaining changes mostly serve to make it clearer to type checkers if/when a variable can/cannot be None.
Mostly some explicit None checks, avoiding variable redefinition and the removal of a circuitous import.
8b4736f
to
e203e04
Compare
Ok, done. I've split the patch into a bunch of commits doing a lot of prep work, then one big patch adding the type hints only and finally one patch integrating |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome, thanks for your work. I tried it out in my project and have some feedback.
KNOWN_RESPONSE_CONTROLS[ldap.CONTROL_ASSERT] = AssertionControl | ||
# FIXME: This is a request control though? | ||
#KNOWN_RESPONSE_CONTROLS[ldap.CONTROL_ASSERT] = AssertionControl |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FIXME should be fixed. Just pointing it out, not sure what to do here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I'm not sure that I'm right here, so I kind of left it as a marker for the maintainers to have a look. I guess the "fix" would be just deleting the lines, but not sure
Mostly some code refactoring to avoid variable redefinition and to add some additional None checks. Make sure that _unparseChangeRecord can handle "bytes | List[bytes]" (since that will be part of the type definition of 3-tuple modifications). Also, change "valid_changetype_dict" to "valid_changetype_set" (since the variable is anyway used as a set, not as a dict).
Mostly some small changes to avoid variable redefinition and changing some functions to remove superfluous return values.
Instead of the magic loop in subentry.py which looks for appropriate classes from Lib/ldap/schema/models.py, let the classes register themselves explicitly in the SCHEMA_CLASS_MAPPING/SCHEMA_ATTR_MAPPING in order to not confuse type checkers (this should also be clearer to humans reading the code). In addition, add some more comments to subentry.py and do the usual type safety fixups (explicit type checks, asserts, avoiding variable redefinitions, etc).
First, rewrite extract_tokens() in Lib/ldap/schema/tokenizer.py as parse_tokens() and document the function. Then, use the new function in Lib/ldap/schema/models.py, and remove the token_defaults class attributes (which confuse type checkers as they can't decide which attributes a given class does/doesn't have) and instead set the attribute defaults explicitly. This might look like a big change, but most of it is repetitive changes throughout the classes in Lib/ldap/schema/models.py.
Small fix to keep type checkers happy.
Essentially, make sure that the cookie is always stored as binary
self.responseValue is bytes, not a str
…p/controls/* Several of the request controls are registered in KNOWN_RESPONSE_CONTROLS, which appears to be an oversight...probably due to a bit too much copy-pasting?
Phew, after all the prep work, the actual type annotations can finally be added.
This is necessary in order for type checking due to: python/typing#1333 Given that _ldap is an internal module, this change is hopefully ok.
This helps in ensuring that the stub and the C module don't drift apart.
e203e04
to
ba337e8
Compare
Ok, I've pushed a new version taking @intgr's comments into account. Most controversial change here is the move of |
@tiran gentle ping? |
Hey, @Alphix, can you update this to resolve conflicts, and then page @droideck, @mistotebe, and @tiran for review? Thanks so much... this is a tremendous contribution that shouldn't be left to bit-rot. |
@macserv Eh, I'm a volunteer, using my free time for this. If you want to push this forward, I'd suggest you try to get in touch with the maintainers...once there's some sign of interest from their side, I'd be happy to rebase and resolve conflicts... |
With this patch applied,
MYPYPATH=/Stubs mypy --strict Lib/
passes without any warnings.tox
also passes for everything frompy37
topy11
(and also includingdoc
andpypy3
).I've tried my best to not make more modifications than strictly necessary. Most of the time, changes merely serve to make life easier for
mypy
(i.e. to enable type inference) or to fixup non-type aware code.The patch might look dauntingly large, but once you cut away the trivial stuff (untyped function definitions which have been replaced with types ones, lots and lots of new import statements, etc) the diff is actually not that big.