-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
Conversation
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.
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 |
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 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 |
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 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())) |
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.
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") |
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.
All other subclasses of InvokeIntegBase assign template
a Path
value, except for this. Good catch by mypy
|
||
telemetry_endpoint_url = os.getenv( | ||
"__SAM_CLI_TELEMETRY_ENDPOINT_URL", "https://aws-serverless-tools-telemetry.us-west-2.amazonaws.com/metrics" | ||
) |
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 change was addressing inconsistent types:
- above:
str
- below:
Optional[str]
@handler.setter | ||
def handler(self, value): | ||
self._function["handler"] = value | ||
|
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.
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) |
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.
Why the join here but {item}
a few lines up? If it doesn't matter I'd go with less code.
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.
yeah it does not matter, I can remove the join, sounds better
requirements/base.txt
Outdated
@@ -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" |
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.
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.
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 agree, but without getting ride of that Dict, mypy cannot infer types in quite some places..
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 was aiming for NamedTuple at first, and then found they need to be mutable
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.
dataclasses
is a pretty reputable and simple portback, I think it is pretty safe to use https://github.com/ericvsmith/dataclasses
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.
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!)
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.
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 😋
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.
They are moved here: aahung@b5e2b1b
@codeuri.setter | ||
def codeuri(self, codeuri): | ||
self._codeuri = codeuri | ||
|
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.
setter / getter needs proximity
samcli/lib/logs/formatter.py
Outdated
|
||
from builtins import map |
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.
Python 2 is no longer used, mypy is not happy to see this failed import
98fafd3
to
8f46e04
Compare
@@ -149,7 +149,6 @@ def _verify_invoke_built_function(self, template_path, function_logical_id, over | |||
|
|||
|
|||
class BuildIntegRubyBase(BuildIntegBase): | |||
EXPECTED_FILES_GLOBAL_MANIFEST = set() |
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 is not being used
SUPPORTED_DEP_MANAGERS = { | ||
c["dependency_manager"] | ||
SUPPORTED_DEP_MANAGERS: Set[str] = { | ||
c["dependency_manager"] # type: ignore |
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 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 |
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 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 |
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.
following the import in requests.utils
, the quote
is actually imported from urllib.parse
(for Python 3)
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.
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.
just manually merged from develop |
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 triggeredtest_download_source_code_binding
is defined twice, and they are identical.format()
in "tests/unit/lib/init/test_init.py"What side effects does this change have?
N/A
Checklist
make pr
passesmake update-reproducible-reqs
if dependencies were changedBy submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.