Skip to content

Various mypy fixes - #5350

Merged
holmanb merged 12 commits into
canonical:mainfrom
holmanb:holmanb/use-normal-mypy-mode
Jul 1, 2024
Merged

holmanb merged 12 commits into
canonical:mainfrom
holmanb:holmanb/use-normal-mypy-mode

Conversation

@holmanb

@holmanb holmanb commented Jun 1, 2024

Copy link
Copy Markdown
Member

Additional Context

See individual commits.

This work enables using more of mypy's static analysis features.

This PR does various cleanups including:

  • remove unnecessary type ignore comments
  • add missing type annotations
  • remove invalid type annotations
  • minor refactors to eliminate the need for type annotations
  • eliminate unused code
  • auto-format network jsonschema
  • tox: fix minor issues
  • tox: add stub libraries

Note to reviewers:

Most of the changes here are no-ops. That said, please pay special attention to the commit titled refactor(typing): Remove unused code paths. Invalid type annotations can lead mypy to warn incorrectly that a code path is unused (this is how I found many of the type errors in this commit series). I do believe that I was thorough in verifying this commit, but this one carries the most risk of the series.

@holmanb
holmanb force-pushed the holmanb/use-normal-mypy-mode branch from b47225a to 80ee3a3 Compare June 3, 2024 21:28
holmanb added a commit to holmanb/cloud-init that referenced this pull request Jun 3, 2024
Remove code for detecting a virtualenv. This has been broken since ~3.7.
holmanb added a commit to holmanb/cloud-init that referenced this pull request Jun 3, 2024
Invalid type annotations confuse mypy.

When it sees a type annotation that doesn't conflict with other
annotations, it is trusted. This means that future attempts to
conditionally check a variable's type will cause mypy to produce an
unreachable code path warning.
@holmanb
holmanb force-pushed the holmanb/use-normal-mypy-mode branch from 202c0ba to a3e9409 Compare June 3, 2024 23:01
holmanb added a commit to holmanb/cloud-init that referenced this pull request Jun 3, 2024
Later use of the same variable name leads mypy to report unreachable code
paths.
holmanb added a commit to holmanb/cloud-init that referenced this pull request Jun 3, 2024
Mypy infers an unannotated variable's type from its initial value.
When a variable's type may change (such as from None to non-None),
the variable's type needs to be annotated, otherwise mypy may warn
of unreachable code paths.
holmanb added a commit to holmanb/cloud-init that referenced this pull request Jun 3, 2024
Python's provides type annotation features which allow validating types
throughout the code base. Use, rather than ignore, these features.

Note that since a dict's .get() may return None, using this method
when the only possible return value is a literal will confuse mypy.
holmanb added a commit to holmanb/cloud-init that referenced this pull request Jun 3, 2024
Enable positional arguments for various environments.
Add missing deps for tip-mypy.
The -tip envs were broken when a non-tip env existed. In these cases,
{[testenv:mypy]commands} resolved to the non-tip environment. Fix it.
holmanb added a commit to holmanb/cloud-init that referenced this pull request Jun 3, 2024
Fix the current network v2 schema as well.
holmanb added a commit to holmanb/cloud-init that referenced this pull request Jun 3, 2024
holmanb added a commit to holmanb/cloud-init that referenced this pull request Jun 3, 2024
@holmanb
holmanb force-pushed the holmanb/use-normal-mypy-mode branch from a3e9409 to c4924a2 Compare June 3, 2024 23:08
holmanb added a commit to holmanb/cloud-init that referenced this pull request Jun 10, 2024
Python's provides type annotation features which allow validating types
throughout the code base. Use, rather than ignore, these features.

Note that since a dict's .get() may return None, using this method
when the only possible return value is a literal will confuse mypy.
holmanb added a commit to holmanb/cloud-init that referenced this pull request Jun 10, 2024
Enable positional arguments for various environments.
Add missing deps for tip-mypy.
The -tip envs were broken when a non-tip env existed. In these cases,
{[testenv:mypy]commands} resolved to the non-tip environment. Fix it.
holmanb added a commit to holmanb/cloud-init that referenced this pull request Jun 10, 2024
Fix the current network v2 schema as well.
holmanb added a commit to holmanb/cloud-init that referenced this pull request Jun 10, 2024
@holmanb
holmanb force-pushed the holmanb/use-normal-mypy-mode branch from d4ad08f to e13fbec Compare June 10, 2024 21:31
holmanb added a commit to holmanb/cloud-init that referenced this pull request Jun 10, 2024
@holmanb
holmanb force-pushed the holmanb/use-normal-mypy-mode branch from e13fbec to 544754d Compare June 10, 2024 21:32
@holmanb
holmanb marked this pull request as ready for review June 10, 2024 21:42
@holmanb
holmanb force-pushed the holmanb/use-normal-mypy-mode branch 2 times, most recently from 891684b to c685400 Compare June 11, 2024 00:10

@TheRealFalcon TheRealFalcon left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I don't have time to do a full review today, but I realized I have a stale "Pending" comment that is still relevant.



def handle(name: str, cfg: Config, cloud: Cloud, args: list) -> None:
def handle(name: str, cfg: Config, cloud: Cloud, _) -> None:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This is part of every single handler...

Let's not update this in this PR. If we do, lets update all handlers in a separate PR. Also, I'd prefer _args: List rather than _. A single underscore doesn't tell you what it's supposed to represent.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This is part of every single handler...

This is the only one that has an argument name which later has a different type. If you'd rather we didn't fix it this way, then I'll change the shadowed variable name.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Ah, I see now. I thought you were doing something else. Yeah, I'm ok changing this one here. I think I'd still prefer the name change though.

holmanb added a commit to holmanb/cloud-init that referenced this pull request Jun 17, 2024
@holmanb holmanb changed the title Holmanb/use normal mypy mode Variouys mypy fixes Jun 17, 2024

@TheRealFalcon TheRealFalcon left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Comments inline

Comment thread .git-blame-ignore-revs Outdated

# Apply json formatting
04c82eafd8fe34c5f02ffecfdb5dba888f3baa3f
e13fbec78315f535b3fccb22727e5035201b6929

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I don't see this commit anywhere.

Comment thread setup_utils.py
Comment thread cloudinit/config/schema.py
Comment thread cloudinit/url_helper.py

@property
def contents(self) -> bytes:
if self._response.content is None:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Do you know why mypy is yelling about this one? content can definitely be None, so I think the check here is valid.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I'll need to dig in further about this one

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

@TheRealFalcon Thanks for finding that. It turns out that this was due to a bug in typeshed. I just submitted a PR to fix it. I'll change this PR to silence this warning until a fix is released.

Comment thread cloudinit/net/netplan.py
Comment thread cloudinit/util.py Outdated
Comment thread cloudinit/config/schemas/schema-network-config-v2.json
@TheRealFalcon TheRealFalcon self-assigned this Jun 19, 2024
holmanb added a commit to holmanb/cloud-init that referenced this pull request Jun 20, 2024
@holmanb holmanb changed the title Variouys mypy fixes Various mypy fixes Jun 21, 2024
holmanb added a commit to holmanb/cloud-init that referenced this pull request Jun 21, 2024
@holmanb

holmanb commented Jun 21, 2024

Copy link
Copy Markdown
Member Author

@TheRealFalcon I think I've addressed all comments. If tests pass, I think this is ready for review.

@holmanb
holmanb requested a review from TheRealFalcon June 21, 2024 02:40

@TheRealFalcon TheRealFalcon left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I left a few more inline. The logging one is the only major one (deleted the comment).

Comment thread .git-blame-ignore-revs Outdated
bae9b11da9ed7dd0b16fe5adeaf4774b7cc628cf

# Apply json formatting
04c82eafd8fe34c5f02ffecfdb5dba888f3baa3f

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

These changed again. Think it might make sense to update these after the fact?

Comment thread tox.ini Outdated
types-setuptools=={[format_deps]types-setuptools}
typing-extensions=={[format_deps]typing-extensions}
commands = {envpython} -m mypy cloudinit/ tests/ tools/
# TODO: add --check-untyped-defs to allow checking untyped function bodies

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This is done as of #5409 , correct?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yes!

holmanb added a commit to holmanb/cloud-init that referenced this pull request Jun 27, 2024
Remove code for detecting a virtualenv. This has been broken since ~3.7.
holmanb added a commit to holmanb/cloud-init that referenced this pull request Jun 27, 2024
Invalid type annotations confuse mypy.

When it sees a type annotation that doesn't conflict with other
annotations, it is trusted. This means that future attempts to
conditionally check a variable's type will cause mypy to produce an
unreachable code path warning.
holmanb added a commit to holmanb/cloud-init that referenced this pull request Jun 27, 2024
Later use of the same variable name leads mypy to report unreachable code
paths.
holmanb added a commit to holmanb/cloud-init that referenced this pull request Jun 27, 2024
Mypy infers an unannotated variable's type from its initial value.
When a variable's type may change (such as from None to non-None),
the variable's type needs to be annotated, otherwise mypy may warn
of unreachable code paths.
@holmanb
holmanb force-pushed the holmanb/use-normal-mypy-mode branch from cb493bd to 3c85e40 Compare June 27, 2024 21:08
holmanb added a commit to holmanb/cloud-init that referenced this pull request Jun 27, 2024
Enable mypy's "warn_unreachable" checks.
Remove adjacent unused mocks.

Note: mypy can incorrectly report typing errors as "unused code paths"
holmanb added a commit to holmanb/cloud-init that referenced this pull request Jun 27, 2024
Python's provides type annotation features which allow validating types
throughout the code base. Use, rather than ignore, these features.

Note that since a dict's .get() may return None, using this method
when the only possible return value is a literal will confuse mypy.
holmanb added a commit to holmanb/cloud-init that referenced this pull request Jun 27, 2024
Enable positional arguments for various environments.
Add missing deps for tip-mypy.
The -tip envs were broken when a non-tip env existed. In these cases,
{[testenv:mypy]commands} resolved to the non-tip environment. Fix it.
holmanb added a commit to holmanb/cloud-init that referenced this pull request Jun 27, 2024
Fix the current network v2 schema as well.
holmanb added a commit to holmanb/cloud-init that referenced this pull request Jun 27, 2024
This doesn't add anything useful on its own, but it defines a custom
type which can easily be overridden.

See canonicalGH-5398 for more details.
@holmanb

holmanb commented Jun 28, 2024

Copy link
Copy Markdown
Member Author

Looks like the recent addition to mypy features expanded the scope slightly, so I had to update some test code to make it pass. I think I just need to fix the merge conflict before this is ready to merge @TheRealFalcon.

@TheRealFalcon TheRealFalcon left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

2 questions inline

Comment thread tests/integration_tests/instances.py Outdated
self,
cloud: "IntegrationCloud",
instance: BaseInstance,
instance,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This was fairly useful when it comes to editor autocomplete. Why does mypy not like it?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Because instance doesn't contain BaseInstance. It contains a child class of BaseInstance which has other attributes and methods, so when we use those attributes mypy (rightfully) warns of errors like this:

tests/integration_tests/modules/test_combined.py:465: error: "BaseInstance" has no attribute "availability_zone"
tests/integration_tests/modules/test_combined.py:486: error: "BaseInstance" has no attribute "zone"
tests/integration_tests/modules/test_combined.py:487: error: "BaseInstance" has no attribute "instance_id"

We can use type narrowing to preserve autocomplete suggestions while satisfying mypy. I'll cook something up.

f"Failed to update packages: {response.stderr}"
)

def ip(self) -> str:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This is used in __exit__ (though the implementation is buggy). Why is it being removed?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The whole purpose of that was to to make it easier for someone to ssh into an instance on a public cloud after a test, however since only lxd has execute_via_ssh, its purpose is defeated. I've been meaning to clean this up for a while, but hadn't gotten to it yet. Mypy is complaining about the types in this code which is why I finally made the change.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

So can we fix it instead of dropping it? It's valid if we don't want to fix it here, but we can use the # typing: ignore directives in that case.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Sure, that's fair. Looking at this again I think the fix might be easier than I previously thought.

holmanb added 12 commits July 1, 2024 11:56
Remove code for detecting a virtualenv. This has been broken since ~3.7.
Invalid type annotations confuse mypy.

When it sees a type annotation that doesn't conflict with other
annotations, it is trusted. This means that future attempts to
conditionally check a variable's type will cause mypy to produce an
unreachable code path warning.
Later use of the same variable name leads mypy to report unreachable code
paths.
Mypy infers an unannotated variable's type from its initial value.
When a variable's type may change (such as from None to non-None),
the variable's type needs to be annotated, otherwise mypy may warn
of unreachable code paths.
Enable mypy's "warn_unreachable" checks.
Remove adjacent unused mocks.

Note: mypy can incorrectly report typing errors as "unused code paths"
Python's provides type annotation features which allow validating types
throughout the code base. Use, rather than ignore, these features.

Note that since a dict's .get() may return None, using this method
when the only possible return value is a literal will confuse mypy.
Enable positional arguments for various environments.
Add missing deps for tip-mypy.
The -tip envs were broken when a non-tip env existed. In these cases,
{[testenv:mypy]commands} resolved to the non-tip environment. Fix it.
Fix the current network v2 schema as well.
This doesn't add anything useful on its own, but it defines a custom
type which can easily be overridden.

See canonicalGH-5398 for more details.
This also satisfies mypy via type narrowing, which hasattr() does not
accomplish.
@holmanb

holmanb commented Jul 1, 2024

Copy link
Copy Markdown
Member Author

@TheRealFalcon Just force pushed to fix the merge conflict, and while I was at it I squashed the fixup commits and updated commit messages with PR number. Assuming tests pass this one is ready for re-review.

@TheRealFalcon TheRealFalcon left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

(deleted original comment...the _log thing keeps confusing me for a second)

LGTM!

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