-
Notifications
You must be signed in to change notification settings - Fork 3
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
mypy: upgrade to 1.10.0 #156
Conversation
Signed-off-by: Achille Roussel <achille.roussel@gmail.com>
Signed-off-by: Achille Roussel <achille.roussel@gmail.com>
Signed-off-by: Achille Roussel <achille.roussel@gmail.com>
@@ -256,9 +256,9 @@ def primitive_func(input: Input) -> Output: | |||
return OneShotScheduler(func).run(input) | |||
|
|||
primitive_func.__qualname__ = f"{name}_primitive" | |||
primitive_func = durable(primitive_func) | |||
durable_primitive_func = durable(primitive_func) |
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.
mypy complained here that the type of primitive_func
didn't match after being wrapped by durable
.
@@ -117,7 +117,7 @@ def do_POST(self): | |||
) | |||
max_age = timedelta(minutes=5) | |||
try: | |||
verify_request(signed_request, verification_key, max_age) | |||
verify_request(signed_request, self.verification_key, max_age) |
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.
This was an actual bug.
@@ -83,7 +83,7 @@ def __str__(self): | |||
_OUTPUT_TYPES: Dict[Type[Any], Callable[[Any], Status]] = {} | |||
|
|||
|
|||
def status_for_error(error: Exception) -> Status: | |||
def status_for_error(error: BaseException) -> Status: |
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.
Turned out KeyboardInterrupt
is not a subclass of Exception
.
self.roundtrips: OrderedDict[DispatchID, List[RoundTrip]] = OrderedDict() | ||
self.collect_roundtrips = collect_roundtrips |
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.
This simplifies call sites where we accessed the roundtrips
field since it cannot be None
anymore.
assert isinstance(verification_key, Ed25519PublicKey) | ||
assert verification_key.public_bytes_raw(), public_key2_bytes |
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.
Using assert
instead of the methods helped mypy understand that verification_key
couldn't be None
.
@@ -65,7 +65,7 @@ def test_conversion_without_traceback(self): | |||
original_exception = e | |||
|
|||
error = Error.from_exception(original_exception) | |||
error.traceback = "" | |||
error.traceback = b"" |
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.
This was an actual bug.
@@ -72,7 +72,7 @@ def test_content_length_missing(self): | |||
def test_content_length_too_large(self): | |||
resp = self.client.post( | |||
f"{self.endpoint}/dispatch.sdk.v1.FunctionService/Run", | |||
data=b"a" * 16_000_001, | |||
data={"msg": "a" * 16_000_001}, |
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.
The post
method apparently expected data
to be an object.
for i, (is_null, value) in enumerate(stack if stack is not None else []): | ||
if is_null: | ||
print(f"stack[{i}] = NULL") | ||
else: | ||
print(f"stack[{i}] = {value}") | ||
print(f"BP = {bp}") | ||
for i, block in enumerate(blocks): | ||
for i, block in enumerate(blocks if blocks is not None else []): |
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.
These would have broken in the case where stack
and blocks
were None
.
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.
Looks like mypy isn't smart enough to see that these are only None
if frame_state >= FRAME_CLEARED
, and that we're in a block where frame_state < FRAME_CLEARED
.
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.
This analysis would be pretty difficult to make right? There are no constants in Python, FRAME_CLEARED
could be altered concurrently by a thread, potentially in a module that imported this one.
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.
I don't think there are any points between checks where the GIL would be released and another thread allowed to run? There are calls to the C extension in there, but you have to explicitly release the GIL from C. mypy either assumes that all C extension calls may release the GIL, or it just doesn't propagate constraints across blocks.
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.
Assumptions about when the GIL can be released seem like asking for trouble, the rules are a bit unclear as to when this may happen; I would expect mypy not to try to be that smart because it's a lot of efforts for unclear rewards (false positives make the program safer anyways).
We could verify by pulling the value into a local instead of depending on the global FRAME_CLEARED
variable.
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.
Oh but even with a local, it doesn't know that frame_state = ext.get_frame_state(g)
will return a value less than FRAME_CLEARED
right?
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.
(I just tried with a local and it still complains that the values might be None
)
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.
stack
and blocks
are set to None
after frame_state
is set, and if and only if frame_state < FRAME_CLEARED
. The frame_state
variable does not change after they're set to None
.
I suspect the issue is that mypy forwards constraints into blocks, but doesn't carry them across blocks, because of potential path explosions. It seems to be smart enough to tighten types as you extend into if
blocks, but in this case it doesn't carry the frame_state < FRAME_CLEARED
constraint forward to an adjacent block.
No big deal, false positives are expected occasionally 🙂
@@ -30,7 +30,7 @@ fmt-check: | |||
exit $$((isort_status + black_status)) | |||
|
|||
typecheck: | |||
$(PYTHON) -m mypy src tests | |||
$(PYTHON) -m mypy --check-untyped-defs src tests examples |
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.
We're doing type checks on the examples
directory as well now.
Signed-off-by: Achille Roussel <achille.roussel@gmail.com>
Signed-off-by: Achille Roussel <achille.roussel@gmail.com>
Signed-off-by: Achille Roussel <achille.roussel@gmail.com>
Signed-off-by: Achille Roussel <achille.roussel@gmail.com>
…om-test-package test: drop FastAPI dependency
This PR upgrade mypy to 1.10.0, and enable
--check-untyped-defs
which does a much stricter pass. I then fixed all the type checks that were newly reported.