Skip to content
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

Improve runtime safety by checking untyped definitions #16785

Open
miketheman opened this issue Sep 24, 2024 · 0 comments
Open

Improve runtime safety by checking untyped definitions #16785

miketheman opened this issue Sep 24, 2024 · 0 comments
Labels
developer experience Anything that improves the experience for Warehouse devs testing Test infrastructure and individual tests

Comments

@miketheman
Copy link
Member

We use mypy to help prevent some runtime errors for passing the wrong types of objects around.

In a recent attempt to replace integer math with a timedelta duration, the change from an int to float returned type worked in almost every case, except for when the value was being passed into a redis setex command as the expiration, which only accepts integers or durations.

The hope was that with that change, if the typed function received the incorrect type, a dev-time error would be raised, however nothing was raised, so we felt it in production, which was rolled back.

Giving mypy enough "hints" to trigger its checks was this diff, which wasn't added to the code yet:

diff --git a/warehouse/sessions.py b/warehouse/sessions.py
index 902e55dcb..591948688 100644
--- a/warehouse/sessions.py
+++ b/warehouse/sessions.py
@@ -224,7 +224,7 @@ class SessionFactory:
     cookie_name = "session_id"
     max_age = 12 * 60 * 60  # 12 hours

-    def __init__(self, secret, url):
+    def __init__(self, secret, url) -> None:
         self.redis = redis.StrictRedis.from_url(url)
         self.signer = crypto.TimestampSigner(secret, salt="session")

@@ -275,7 +275,7 @@ class SessionFactory:

         return session

-    def _process_response(self, request, response):
+    def _process_response(self, request, response) -> None:
         # If the request has an InvalidSession, then the view can't have
         # accessed the session, and we can just skip all of this anyways.
         if isinstance(request.session, InvalidSession):

Had these been in place, the check would have failed, and we may have prevented that issue.

However, applying hints selectively everywhere isn't a good enough safety net, at least until we have a 100% type coverage, which in and of itself isn't always a great goal.

Taking a hint from one of the --strict settings of mypy, we can enable checking the bodies of untyped signatures with --check-untyped-defs so the checks would happen even if we don't explicitly add types, leaning on type inference, even if we don't add type hints everywhere.

An example in mypy playground that demonstrates the issue, once without the flag (passes checks), and once with the flag added, which correctly fails the check. (once loaded, click Run to see the result).

Running on current HEAD (85bb371) produces:

Found 202 errors in 48 files (checked 386 source files)

These need to be corrected before we can add this safety net into our test suite.

@miketheman miketheman added testing Test infrastructure and individual tests developer experience Anything that improves the experience for Warehouse devs labels Sep 24, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
developer experience Anything that improves the experience for Warehouse devs testing Test infrastructure and individual tests
Projects
None yet
Development

No branches or pull requests

1 participant