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

Fix the use of setup's '--profile-self' path … #11934

Merged
merged 1 commit into from
Jul 4, 2023

Conversation

GertyP
Copy link
Contributor

@GertyP GertyP commented Jul 2, 2023

... after recent change to the backend 'generate(...) ' signature for '--genvslite': profile.runctx('...') now constructs the correct cmd string and also extracts the return result from 'locals()'.

This is in reference to this PR comment

mypy's type-checking was complaining that this '_generate(...) -> T.Optional[dict]:' func now might be returning the Any type, after, I presume, seeing me assign the result out of the 'locals()' dictionary to 'captured_compile_args'. I thought mypy is supposed to deduce type narrowing with 'asserts' and 'isinstance', but that doesn't seem to be the case here 🤷‍♂️ so I've had to add a mypy type check error suppression comment on the return line.

@GertyP GertyP requested a review from jpakkane as a code owner July 2, 2023 21:10
'capture=capture,'
'captured_compile_args_per_buildtype_and_target=captured_compile_args_per_buildtype_and_target)', globals(), locals(), filename=fname)
captured_compile_args = locals()['gen_result']
assert ((captured_compile_args is None) or isinstance(captured_compile_args, dict))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not a real assert, which may explain why mypy didn't do type narrowing on it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's incredibly helpful.
It looks like an assert, it parses/interprets without any complaint, and it also behaves correctly -

>>> assert (({} is None) or isinstance({}, dict))
>>> assert ((None is None) or isinstance(None, dict))
>>> assert ((123 is None) or isinstance(123, dict))
... AssertionError

So would you care to share your insight on what makes this not a real assert?

Copy link
Member

@eli-schwartz eli-schwartz Jul 3, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's rewrite this assert to be semantically identical, but more easily readable.

isnone: bool = captured_compile_args is None
isdict: bool = isinstance(captured_compile_args, dict)
test_statement: T.Tuple[bool] = (isnone or isdict, )

assert test_statement

This doesn't generate the same bytecode, but the only difference is that it needs to STORE_NAME before the assert.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(I think the other issue is that mypy can't do type narrowing on or statements, but can do it when each is a separate statement.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have quite different definitions of easily readable 😄

Even with the those contortions to seemingly try to spoon-feed mypy, it still thinks we'll can end up returning an Any type... But I suppose I (and now you also) must be misunderstanding the assert and/or not providing sufficient information to mypy to deduce narrow the type with the assert.

Also, you haven't explained why this is not a real assert. As far as I can see, everything about it does what it should and that all signs point to a limitation with mypy's type deduction , but I'd still love to learn what makes it an incorrect or not real assert.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

054e714 seems to be enough of a dance, to get mypy to figure things out.

@@ -308,7 +312,8 @@ def _generate(self,
os.unlink(cdf)
raise

return captured_compile_args
# Even with the asserts, mypy's type checking thinks this could be 'Any' type, not the None|dict we know it to be, hence the suppression comment -
return captured_compile_args # type: ignore[no-any-return]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you're suppressing errors there's a 99% chance you misunderstood how things work. In the remaining 1% case you should have a better comment than "I thought this was supposed to work but it doesn't so just ignore it" to explain why.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's intriguing. Supposing we follow through your 1% case scenario, where there actually is no misunderstanding and we actually have given mypy everything needed to deduce the type that it's getting wrong: You say you don't like a comment that explains why we think mypy has enough to deduce a type but which it fails to deduce. So what are you proposing would be a better kind of comment in this scenario?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem is "but which it fails to deduce".

If you've given it everything it needs to deduce the type, but it isn't deducing it, then either the code isn't giving it the information you think it is, or else the mypy documentation should discuss the caveat and serve as a reference, or else it's a bug in mypy and deserves to be reported.

(Either way using a cast is often better.)

Copy link
Contributor Author

@GertyP GertyP Jul 3, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, this statement would be a lot easier to swallow if even just 1 of the currently 30 instances of # type: ignore that are scattered throughout meson had any hint of an accompanying explanation and/or apology, never even mind their total absence of any reference to a mypy bug that, according to you, must be used if one has to resort to suppressing a mypy type warning for something that otherwise definitely does provide enough type info, that leaves a mypy bug as the only remaining conclusion.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ha. Well, I've found 1 instance of this in mtest.py, ln 126.

Copy link
Member

@eli-schwartz eli-schwartz Jul 3, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case it seems the solution was to annotate the original value as not just None, but T.Optional[TheOtherThingItCouldBe], so probably not a mypy bug, and not even a dance. This is the problem with being quick to mark a test as "expected to fail", especially in newly added code rather than when adding tests after the fact.

e.g. arglist.py

TODO: fix 'Invalid index type' and 'Incompatible types in assignment' erros

which was added in a PR that exclusively added typing to existing code, and inherently has more lax standards; in the generic case, when adding tests to existing code, it's okay to mark them as expectedfail "but we do not know why".

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See commits 1 and 3 here: #11938

I added comments to 3 cases, and removed 3 more entirely because it turns out we can check the mixins.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Many of those mypy work-around excuse comments you quote look very much like this scenario, by which I mean they certainly don't reference a mypy bug that you eventually said is what should be used to accompany these suppressions.

This -

Mypy doesn't follow hasattr, and it's pretty easy to visually inspect that this is correct, so we'll just ignore it.

describes a very similar scenario to what's been going on here. In my case, mypy doesn't follow some pretty clear to see type narrowing deduction in what is (until anyone suggests otherwise ... i.e. I'm still waiting; are you still insisting it's not a real assert?), valid and clear python.

My impression is that it's actually quite misleading and unhelpful to say "there's a 99% chance you misunderstood how things work". Seems like it's more a case of: mypy's type checking can simply be wrong, but with some persistence and wrangling/restructuring - perhaps even contortions 😄- of the code, it might be possible to get mypy to figure things out.

In future, and especially if it can be explained in a sentence or two, if you have insight on what you think is a misunderstanding, there's a 100% chance that it'll be more useful and productive to share that insight, rather than leaving nothing more than a, "That's not a real assert" kind of remark. This whole exchange could have been far simpler if you'd have just said something like -

"It looks like you're not giving enough type info to mypy. Here's why I think this: ..."

or -

"Use of '# type: ignore' should be a last resort if you think you've given mypy enough to go on, [which it looks like you haven't because ...] / [which it looks like you have but I can't suggest what more you could do to help it out]. Any last-resort suppressions should be accompanied with a good explanation and preferably (but not actually required, as evidence by the many existing unaccompanied uses) a comment pointing to a mypy bug".

Copy link
Contributor Author

@GertyP GertyP Jul 3, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so probably not a mypy bug

And by the way, if it's valid code from which the reader can trivially deduce type-correctness, then I'd say it very much is a mypy bug. Call it a limitation if you prefer, but a work-around from valid, type-correct code is still a work-around.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

describes a very similar scenario to what's been going on here. In my case, mypy doesn't follow some pretty clear to see type narrowing deduction

The difference is that not everything is, in fact, a type narrowing deduction:

  • mypy literally does not implement hasattr and is expected to get it wrong for the reason in the comment
  • whereas it is "supposed" to type narrow on isinstance / x is None and was simply failing to do so, here, and the comment is saying (paraphrased):

    this code is correct and mypy supports it and correctly analyzes it. Except actually it does not and I'm not sure why

preferably (but not actually required, as evidence by the many existing unaccompanied uses) a comment pointing to a mypy bug

But also I didn't insist on an url comment, I merely said that there should exist references. I had intended to mean, by that, that it should be possible to read the mypy documentation / bugtracker and conclude some authoritative fact like "mypy does not support hasattr, so we don't even try to type narrow" or "this is definitely a mypy bug because pylance gets it correct, thereby proving the annotation is not faulty" or "this is a bug, here's an url link describing why". Apologies for the confusion.

Again, this is no different from e.g. a unittest. If a PR adds code that causes a unittest to fail, and you want to mark the unittest as "expectedfail", I expect some form of explanation for why the unittest failure isn't a problem, and that explanation cannot be "I believe I did everything right, the test still fails, it must be a problem in the test software". But "I definitely 100% can verify that the test software is buggy because it doesn't know how to frobnicate the wolpertinger and this test does that" would be a good explanation.

If in doubt, I would prefer that rather than "use type: ignore as a last resort" / "mark this test as expectedfail" a self-review comment is made saying "this test fails and I'm not sure why, any ideas?"

After the recent change to the backend `generate(...)` signature for
'--genvslite', `profile.runctx('...')` needs to construct the correct
cmd string and also extract the return result from `locals()`.
@eli-schwartz eli-schwartz merged commit c34ee37 into mesonbuild:master Jul 4, 2023
@eli-schwartz
Copy link
Member

Thanks, merged.

n.b. https://cbea.ms/git-commit/

(Commits manually squashed and commit message reformatted to match the blogpost guidance.)

@GertyP GertyP deleted the profile_fix branch July 14, 2023 11:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants