-
-
Notifications
You must be signed in to change notification settings - Fork 582
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
Type annotate format checker methods #958
Conversation
Codecov Report
@@ Coverage Diff @@
## main #958 +/- ##
=======================================
Coverage 96.88% 96.89%
=======================================
Files 20 20
Lines 3276 3284 +8
Branches 449 449
=======================================
+ Hits 3174 3182 +8
Misses 79 79
Partials 23 23
Continue to review full report at Codecov.
|
There are two failures I see in the build, one of which is I'm not sure how best to resolve. We need the type var to correctly annotate these decorators because that is how we instruct type-checkers that the decorator does not change the type of the decorated function. But the type var is not a symbol which we want to export and have I think one option would be to rename the var to |
The goal of this work is to apply annotations in `jsonschema._format` which match the new additions to the type stubs recently added to typeshed. In short, it defines a `_FormatCheckCallable` as a callable -- typically function -- which takes any object and returns a bool. All of the `is_*` functions are `_FormatCheckCallable`s, and the decorators are carefully annotated as being type-preserving using a typevar. Because the returned objects from these functions are not always bools, this changeset now calls `bool()` on those which are not (e.g. `ipaddress.IPv4Address`). This is really just an explicit form of the check which is going to happen in `conforms` and `check` anyway, so there's no significant new cost. The advantage of this is that we have documented (via the annotations) what a format check function is supposed to do: it returns a bool. We could equally well return `Any` from these functions, relying on `__bool__`, but this could confuse new contributors and users. One unfortunate side effect of these changes is that `FormatChecker.cls_checks` needs to be expanded into a full duplicate of `FormatChecker.checks`. `mypy` isn't able to properly understand what `cls_checks = classmethod(checks)` does -- this is part of a class of semi-sophisticated callable and method manipulations that are known to be problematic for `mypy`. In order to get the annotations correct, the simplest solutions are either to annotate it explicitly (cast or type comment) or to expand it as this changeset has done.
00b33bb
to
8d6e113
Compare
Hey! Thanks. Will have a look in another day or two, been a bit busy with other things, but appreciated! (And yeah will offer some more educated opinion on the Sphinx thing then hopefully). |
Bleh. OK, fair enough I guess. These functions, as you note, have 2 different signatures -- the "public API" version of what you give to Willing still to ignore this as we continue but yeah hoping we don't end up with death by a thousand type checker dumbness paper cuts :) (honestly though I'm sort of expecting that -- the library is pretty well tested, so I'm not expecting type checking to reveal any bugs, I'm simply expecting it to help improve documentation, and the above signature for |
Let's perhaps try sphinx-autodoc-typehints which seems to resolve the nitpick here. I pushed a commit, let's see if the rest of CI passes. |
* main: Slightly speed up pip installs by skipping the version check in CI. Remove the now-unused MANIFEST.in. v4.6.0 -> CHANGELOG Ignore the badge URLs, they seem super unreliable from CI. Ignore a deprecation warning coming from pkg_resources on 3.11 Add basic CONTRIBUTING guidelines. Make project.urls be valid URLs. Combine the CI and packaging workflows. Let RTD be authoritative about what the default doc version is. Re-enable Python 3.11 testing in CI. Modernize the packaging setup via PEP 621 and Hatch. Update various GHA versions. Update docs requirements. Revert "Merge pull request python-jsonschema#954 from ssbarnea/fix/py.typed"
FWIW, even as a big proponent of typing for new projects (where you suffer these issues very gradually in exchange for powerful linting on less thoroughly tested code), I find this stuff annoying. Still, I want to try to push through. Having type annotations published by the library will be valuable if it's ever necessary to change the API in v5.0 or later. There are two other solutions we can take for the
Even though the functions are annotated as
If we add some special type like class _ImplementsBool(typing.Protocol):
def __bool__(self) -> bool: ... it might be possible to explain the situation to Then we could remove the This is a behavior I'd like |
Will the second one work? Don't forget there's other ways than implementing |
Agree and on-board with what you started with though btw! Appreciated. |
I think it might work! 🤣 I don't want to drag I think I might come down, after seeing Looks like that autodoc-typehints extension worked! Or, at least, it passed the build. |
OK cool sounds good to me :D Let me give this a quick once over and then will merge then. |
The goal of this work is to apply annotations in
jsonschema._format
which match the new additions to the type stubs recently added to typeshed.In short, it defines a
_FormatCheckCallable
as a callable -- typically function -- which takes any object and returns a bool. All of theis_*
functions are_FormatCheckCallable
s, and the decorators are carefully annotated as being type-preserving using a typevar.Because the returned objects from these functions are not always bools, this changeset now calls
bool()
on those which are not (e.g.ipaddress.IPv4Address
). This is really just an explicit form of the check which is going to happen inconforms
andcheck
anyway, so there's no significant new cost. The advantage of this is that we have documented (via the annotations) what a format check function is supposed to do: it returns a bool. We could equally well returnAny
from these functions, relying on__bool__
, but this could confuse new contributors and users.One unfortunate side effect of these changes is that
FormatChecker.cls_checks
needs to be expanded into a full duplicate ofFormatChecker.checks
.mypy
isn't able to properly understand whatcls_checks = classmethod(checks)
does -- this is part of a class of semi-sophisticated callable and method manipulations that are known to be problematic formypy
. In order to get the annotations correct, the simplest solutions are either to annotate it explicitly (cast or type comment) or to expand it as this changeset has done.When I first saw
return ipaddress.IPv4Address(...)
, I thought it was a bug. Because all of these methods "look like" they return bools, it was confusing to see one which returns anything else. I was excited for a hot second that we'd have our first type-annotation driven bugfix! But then, of course, I realized that__bool__
behavior protects us from any issues. I think that does give some credence to the idea that this clarifies the behavior, FWIW.