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

ci: Enable basic mypy typechecking #2385

Merged
merged 2 commits into from
Nov 19, 2020
Merged

ci: Enable basic mypy typechecking #2385

merged 2 commits into from
Nov 19, 2020

Conversation

aahung
Copy link
Contributor

@aahung aahung commented Nov 18, 2020

Which issue(s) does this change fix?

Why is this change necessary?

We don't have typecheck, typecheck can make our code safer.

How does it address the issue?

Warnings and errors output by mypy without changing any code

I include a demo change how the code is safer without using Dict directly:

With the intro of the following dataclass, mypy can typecheck them so we won't mis-spell dict keys.

  • TemplateOption
  • RuntimeDepInfo
    this change has been separated from this PR and moved to aahung@b5e2b1b
Some benefits we see immediately:
  • test_configure_samcli_logger is defined twice, and one of which cannot be triggered
  • test_download_source_code_binding is defined twice, and they are identical
  • mypy also caught a bunch of incorrect use of .format() in "tests/unit/lib/init/test_init.py"
  • it caught a Python 2 import
  • it enforces "getter" and "setter" should be next to each other, increasing readability instantly
  • it caught some imports that are discouraged by package developers

What side effects does this change have?

N/A

Checklist

  • Write design document (Do I need to write a design document?)
  • Write unit tests
  • Write/update functional tests
  • Write/update integration tests
  • make pr passes
  • make update-reproducible-reqs if dependencies were changed
  • Write documentation

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

Copy link
Contributor Author

@aahung aahung left a comment

Choose a reason for hiding this comment

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

Here are some notes about why I made these changes

@@ -251,7 +251,7 @@ def binary_media_types(self):

_CorsTuple = namedtuple("Cors", ["allow_origin", "allow_methods", "allow_headers", "max_age"])

_CorsTuple.__new__.__defaults__ = (
_CorsTuple.__new__.__defaults__ = ( # type: ignore
Copy link
Contributor Author

Choose a reason for hiding this comment

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

mypy won't acknowledge private attribute. So we need to disable it before we find an alternative

@@ -2,7 +2,7 @@

import click

from boto3 import Session
from boto3.session import Session
Copy link
Contributor Author

Choose a reason for hiding this comment

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

mypy will be happier if we can import from the original class declaration file

for c in list(itertools.chain(*(RUNTIME_DEP_TEMPLATE_MAPPING.values())))
if c["dependency_manager"]
c.dependency_manager
for c in list(itertools.chain.from_iterable(RUNTIME_DEP_TEMPLATE_MAPPING.values()))
Copy link
Contributor Author

Choose a reason for hiding this comment

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

chain.from_iterable is better at perserving types than chain(*

@@ -439,7 +439,7 @@ def test_invoke_returns_expected_results_from_git_function_with_parameters(self)


class TestSamInstrinsicsAndPlugins(InvokeIntegBase):
template = "template-pseudo-params.yaml"
template = Path("template-pseudo-params.yaml")
Copy link
Contributor Author

Choose a reason for hiding this comment

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

All other subclasses of InvokeIntegBase assign template a Path value, except for this. Good catch by mypy

@aahung aahung requested review from jfuss and a team November 18, 2020 17:36
Comment on lines +20 to +23

telemetry_endpoint_url = os.getenv(
"__SAM_CLI_TELEMETRY_ENDPOINT_URL", "https://aws-serverless-tools-telemetry.us-west-2.amazonaws.com/metrics"
)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

this change was addressing inconsistent types:

  • above: str
  • below: Optional[str]

Comment on lines +134 to +137
@handler.setter
def handler(self, value):
self._function["handler"] = value

Copy link
Contributor Author

Choose a reason for hiding this comment

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

setter / getter needs proximity

@@ -527,7 +530,7 @@ def test_fn_sub_second_argument_invalid_formats(self, name, intrinsic):

@parameterized.expand(
[
("If Fn::Sub is a list, it should only have 2 arguments".format(item), item)
(f"If Fn::Sub is a list, it should only have 2 arguments {', '.join(item)}", item)
Copy link
Contributor

Choose a reason for hiding this comment

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

Why the join here but {item} a few lines up? If it doesn't matter I'd go with less code.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yeah it does not matter, I can remove the join, sounds better

@@ -15,3 +15,4 @@ requests==2.23.0
serverlessrepo==0.1.10
aws_lambda_builders==1.1.0
tomlkit==0.7.0
dataclasses~=0.8; python_version<"3.7"
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there no other reasonable way than introducing a new dependency and using dataclasses? It seems like a reasonable change, but would be nice for the first PR to only change what's absolutely necessary.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I agree, but without getting ride of that Dict, mypy cannot infer types in quite some places..

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I was aiming for NamedTuple at first, and then found they need to be mutable

Copy link
Contributor Author

Choose a reason for hiding this comment

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

dataclasses is a pretty reputable and simple portback, I think it is pretty safe to use https://github.com/ericvsmith/dataclasses

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah fair enough if not using it would be a significant detriment. I'm assuming without this mypy would complain a lot?

(Also if we merge this we should let the team know they're free to use dataclasses!)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You know what, I changed my mind! I think it is a great suggestion to make this PR easier to go through! I made another branch with these changes and will file a PR immediately after this one is merged 😋

Copy link
Contributor Author

Choose a reason for hiding this comment

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

They are moved here: aahung@b5e2b1b

Comment on lines +197 to +200
@codeuri.setter
def codeuri(self, codeuri):
self._codeuri = codeuri

Copy link
Contributor Author

Choose a reason for hiding this comment

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

setter / getter needs proximity

Comment on lines 8 to 9

from builtins import map
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Python 2 is no longer used, mypy is not happy to see this failed import

@aahung aahung force-pushed the enable-mypy branch 2 times, most recently from 98fafd3 to 8f46e04 Compare November 18, 2020 18:15
@@ -149,7 +149,6 @@ def _verify_invoke_built_function(self, template_path, function_logical_id, over


class BuildIntegRubyBase(BuildIntegBase):
EXPECTED_FILES_GLOBAL_MANIFEST = set()
Copy link
Contributor Author

Choose a reason for hiding this comment

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

this is not being used

@aahung aahung requested review from CoshUS and sriram-mv November 18, 2020 18:27
SUPPORTED_DEP_MANAGERS = {
c["dependency_manager"]
SUPPORTED_DEP_MANAGERS: Set[str] = {
c["dependency_manager"] # type: ignore
Copy link
Contributor Author

Choose a reason for hiding this comment

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

mypy infers it as Any

itertools.chain(*[c["runtimes"] for c in list(itertools.chain(*(RUNTIME_DEP_TEMPLATE_MAPPING.values())))])
RUNTIMES: Set[str] = set(
itertools.chain(
*[c["runtimes"] for c in list(itertools.chain(*(RUNTIME_DEP_TEMPLATE_MAPPING.values())))] # type: ignore
Copy link
Contributor Author

Choose a reason for hiding this comment

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

mypy infers it as Any

@@ -5,7 +5,7 @@
import os
import json
import base64
from requests.utils import quote as url_quote
from urllib.parse import quote as url_quote
Copy link
Contributor Author

Choose a reason for hiding this comment

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

following the import in requests.utils, the quote is actually imported from urllib.parse (for Python 3)

Copy link
Contributor

@hoffa hoffa left a comment

Choose a reason for hiding this comment

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

Great PR, thanks! 🙌 Good for ensuring long-term health.

We should also make it a soft policy to add missing types to any code touched by new PRs, so we can cover the important stuff fairly quick.

@hoffa hoffa changed the title chore: Enable basic mypy typechecking ci: Enable basic mypy typechecking Nov 18, 2020
@aahung
Copy link
Contributor Author

aahung commented Nov 18, 2020

just manually merged from develop

@aahung aahung merged commit a7b5389 into aws:develop Nov 19, 2020
aahung added a commit that referenced this pull request Nov 20, 2020
…future (#2396)

* Revert "Remove import from builtins"

This reverts commit 0ecbbe4.

* Revert "chore: Enable basic mypy typechecking"

This reverts commit 9d29984.
@aahung aahung mentioned this pull request Dec 23, 2020
7 tasks
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.

3 participants