diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3142342..2871aeb 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,17 +2,17 @@ exclude: ^(buildspec.yml|.pre-commit-config.yaml)$ fail_fast: true repos: - repo: https://github.com/pre-commit/mirrors-isort - rev: v4.3.17 + rev: v5.10.1 hooks: - id: isort # language_version: python3.6 - repo: https://github.com/psf/black - rev: 22.3.0 + rev: 22.8.0 hooks: - id: black exclude: templates/ - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v2.0.0 + rev: v4.1.0 hooks: - id: check-case-conflict - id: end-of-file-fixer @@ -20,6 +20,16 @@ repos: args: - --fix=lf - id: trailing-whitespace + - id: pretty-format-json + args: + - --autofix + - --indent=4 + - --no-sort-keys + - id: check-merge-conflict + - id: check-yaml +- repo: https://github.com/pycqa/flake8 + rev: "5.0.4" + hooks: - id: flake8 additional_dependencies: - flake8-bugbear>=19.3.0 @@ -30,28 +40,21 @@ repos: - flake8-pep3101>=1.2.1 # language_version: python3.6 exclude: templates/ - - id: pretty-format-json - args: - - --autofix - - --indent=4 - - --no-sort-keys - - id: check-merge-conflict - - id: check-yaml - repo: https://github.com/pre-commit/pygrep-hooks - rev: v1.3.0 + rev: v1.9.0 hooks: - id: python-check-blanket-noqa - id: python-check-mock-methods - id: python-no-log-warn - repo: https://github.com/PyCQA/bandit - rev: "1.6.2" + rev: "1.7.1" hooks: - id: bandit files: ^(src|python)/ additional_dependencies: - "importlib-metadata<5" # https://github.com/PyCQA/bandit/issues/956 - repo: https://github.com/pre-commit/mirrors-mypy - rev: v0.790 + rev: v0.812 hooks: - id: mypy files: ^src/ diff --git a/mypy.ini b/mypy.ini new file mode 100644 index 0000000..e744529 --- /dev/null +++ b/mypy.ini @@ -0,0 +1,5 @@ +[mypy] + +# Per-module options: +[mypy-setuptools.*] +ignore_missing_imports = True diff --git a/python/rpdk/python/codegen.py b/python/rpdk/python/codegen.py index 47f475d..0ceaab8 100644 --- a/python/rpdk/python/codegen.py +++ b/python/rpdk/python/codegen.py @@ -1,14 +1,10 @@ +import docker import logging import os import shutil import zipfile -from pathlib import PurePosixPath -from subprocess import PIPE, CalledProcessError, run as subprocess_run # nosec -from tempfile import TemporaryFile -from typing import Dict - -import docker from docker.errors import APIError, ContainerError, ImageLoadError +from pathlib import PurePosixPath from requests.exceptions import ConnectionError as RequestsConnectionError from rpdk.core.data_loaders import resource_stream from rpdk.core.exceptions import DownstreamError, SysExitRecommendedError @@ -16,6 +12,9 @@ from rpdk.core.jsonutils.resolver import ContainerType, resolve_models from rpdk.core.plugin_base import LanguagePlugin from rpdk.core.project import ARTIFACT_TYPE_HOOK +from subprocess import PIPE, CalledProcessError, run as subprocess_run # nosec +from tempfile import TemporaryFile +from typing import Dict from . import __version__ from .resolver import contains_model, translate_type diff --git a/python/rpdk/python/templates/handlers.py b/python/rpdk/python/templates/handlers.py index 7804daf..adf56fe 100644 --- a/python/rpdk/python/templates/handlers.py +++ b/python/rpdk/python/templates/handlers.py @@ -1,6 +1,5 @@ import logging from typing import Any, MutableMapping, Optional - from {{support_lib_pkg}} import ( Action, HandlerErrorCode, diff --git a/python/rpdk/python/templates/hook_handlers.py b/python/rpdk/python/templates/hook_handlers.py index 6a1321d..ceab204 100644 --- a/python/rpdk/python/templates/hook_handlers.py +++ b/python/rpdk/python/templates/hook_handlers.py @@ -1,6 +1,5 @@ import logging from typing import Any, MutableMapping, Optional - from {{support_lib_pkg}} import ( BaseHookHandlerRequest, HandlerErrorCode, diff --git a/python/rpdk/python/templates/hook_models.py b/python/rpdk/python/templates/hook_models.py index 0d0f5d0..f4de610 100644 --- a/python/rpdk/python/templates/hook_models.py +++ b/python/rpdk/python/templates/hook_models.py @@ -1,6 +1,11 @@ # DO NOT modify this file by hand, changes will be overwritten -import sys from dataclasses import dataclass + +from cloudformation_cli_python_lib.interface import BaseHookHandlerRequest, BaseModel +from cloudformation_cli_python_lib.recast import recast_object +from cloudformation_cli_python_lib.utils import deserialize_list + +import sys from inspect import getmembers, isclass from typing import ( AbstractSet, @@ -14,10 +19,6 @@ TypeVar, ) -from cloudformation_cli_python_lib.interface import BaseHookHandlerRequest, BaseModel -from cloudformation_cli_python_lib.recast import recast_object -from cloudformation_cli_python_lib.utils import deserialize_list - T = TypeVar("T") diff --git a/python/rpdk/python/templates/models.py b/python/rpdk/python/templates/models.py index ad36eda..0e7d74d 100644 --- a/python/rpdk/python/templates/models.py +++ b/python/rpdk/python/templates/models.py @@ -1,6 +1,14 @@ # DO NOT modify this file by hand, changes will be overwritten -import sys from dataclasses import dataclass + +from cloudformation_cli_python_lib.interface import ( + BaseModel, + BaseResourceHandlerRequest, +) +from cloudformation_cli_python_lib.recast import recast_object +from cloudformation_cli_python_lib.utils import deserialize_list + +import sys from inspect import getmembers, isclass from typing import ( AbstractSet, @@ -14,13 +22,6 @@ TypeVar, ) -from cloudformation_cli_python_lib.interface import ( - BaseModel, - BaseResourceHandlerRequest, -) -from cloudformation_cli_python_lib.recast import recast_object -from cloudformation_cli_python_lib.utils import deserialize_list - T = TypeVar("T") diff --git a/python/rpdk/python/templates/target_model.py b/python/rpdk/python/templates/target_model.py index f83d2cf..54c2863 100644 --- a/python/rpdk/python/templates/target_model.py +++ b/python/rpdk/python/templates/target_model.py @@ -1,6 +1,11 @@ # DO NOT modify this file by hand, changes will be overwritten -import sys from dataclasses import dataclass + +from cloudformation_cli_python_lib.interface import BaseModel +from cloudformation_cli_python_lib.recast import recast_object +from cloudformation_cli_python_lib.utils import deserialize_list + +import sys from inspect import getmembers, isclass from typing import ( AbstractSet, @@ -14,10 +19,6 @@ TypeVar, ) -from cloudformation_cli_python_lib.interface import BaseModel -from cloudformation_cli_python_lib.recast import recast_object -from cloudformation_cli_python_lib.utils import deserialize_list - T = TypeVar("T") diff --git a/setup.py b/setup.py index 284e988..41bda7c 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,6 @@ """Python 3.6 and 3.7 language support for the CloudFormation CLI""" import os.path import re - from setuptools import setup HERE = os.path.abspath(os.path.dirname(__file__)) diff --git a/src/cloudformation_cli_python_lib/boto3_proxy.py b/src/cloudformation_cli_python_lib/boto3_proxy.py index 772d453..74ff100 100644 --- a/src/cloudformation_cli_python_lib/boto3_proxy.py +++ b/src/cloudformation_cli_python_lib/boto3_proxy.py @@ -1,8 +1,8 @@ # boto3 doesn't have stub files -from typing import Optional - from boto3.session import Session # type: ignore +from typing import Optional + from .utils import Credentials diff --git a/src/cloudformation_cli_python_lib/cipher.py b/src/cloudformation_cli_python_lib/cipher.py index 56854ba..2bb071c 100644 --- a/src/cloudformation_cli_python_lib/cipher.py +++ b/src/cloudformation_cli_python_lib/cipher.py @@ -1,12 +1,10 @@ -import base64 -import json -import uuid -from typing import Optional - # boto3, botocore, aws_encryption_sdk don't have stub files import boto3 # type: ignore import aws_encryption_sdk # type: ignore +import base64 +import json +import uuid from aws_encryption_sdk.exceptions import AWSEncryptionSDKClientError # type: ignore from aws_encryption_sdk.identifiers import CommitmentPolicy # type: ignore from botocore.client import BaseClient # type: ignore @@ -16,6 +14,7 @@ create_assume_role_refresher, ) from botocore.session import Session, get_session # type: ignore +from typing import Optional from .exceptions import _EncryptionError from .utils import Credentials diff --git a/src/cloudformation_cli_python_lib/interface.py b/src/cloudformation_cli_python_lib/interface.py index 225665e..51c6551 100644 --- a/src/cloudformation_cli_python_lib/interface.py +++ b/src/cloudformation_cli_python_lib/interface.py @@ -1,6 +1,7 @@ # pylint: disable=invalid-name -import logging from dataclasses import dataclass + +import logging from enum import Enum, auto from typing import Any, List, Mapping, MutableMapping, Optional, Type diff --git a/src/cloudformation_cli_python_lib/log_delivery.py b/src/cloudformation_cli_python_lib/log_delivery.py index 3a38a0c..95ee60b 100644 --- a/src/cloudformation_cli_python_lib/log_delivery.py +++ b/src/cloudformation_cli_python_lib/log_delivery.py @@ -24,7 +24,7 @@ def __init__( self.group = group self.stream = stream.replace(":", "__") self.client = session.client("logs") - self.sequence_token = "" + self.sequence_token = "" # nosec @classmethod def _get_existing_logger(cls) -> Optional["ProviderLogHandler"]: diff --git a/src/cloudformation_cli_python_lib/metrics.py b/src/cloudformation_cli_python_lib/metrics.py index 6b4af67..df9e60b 100644 --- a/src/cloudformation_cli_python_lib/metrics.py +++ b/src/cloudformation_cli_python_lib/metrics.py @@ -1,8 +1,7 @@ import datetime import logging -from typing import Any, List, Mapping, Optional, Union - from botocore.exceptions import ClientError # type: ignore +from typing import Any, List, Mapping, Optional, Union from .boto3_proxy import SessionProxy from .interface import Action, HookInvocationPoint, MetricTypes, StandardUnit diff --git a/src/cloudformation_cli_python_lib/utils.py b/src/cloudformation_cli_python_lib/utils.py index 0a08db4..0ef80de 100644 --- a/src/cloudformation_cli_python_lib/utils.py +++ b/src/cloudformation_cli_python_lib/utils.py @@ -1,6 +1,7 @@ # pylint: disable=invalid-name -import json from dataclasses import dataclass, field, fields + +import json from datetime import date, datetime, time from typing import ( Any, diff --git a/tests/lib/cipher_test.py b/tests/lib/cipher_test.py index 2920150..ee451cc 100644 --- a/tests/lib/cipher_test.py +++ b/tests/lib/cipher_test.py @@ -1,12 +1,11 @@ # pylint: disable=wrong-import-order,line-too-long -from unittest.mock import Mock, patch - import pytest from cloudformation_cli_python_lib.cipher import KmsCipher from cloudformation_cli_python_lib.exceptions import _EncryptionError from cloudformation_cli_python_lib.utils import Credentials from aws_encryption_sdk.exceptions import AWSEncryptionSDKClientError +from unittest.mock import Mock, patch def mock_session(): diff --git a/tests/lib/exceptions_test.py b/tests/lib/exceptions_test.py index c51971a..6796515 100644 --- a/tests/lib/exceptions_test.py +++ b/tests/lib/exceptions_test.py @@ -1,9 +1,9 @@ -import importlib -import inspect - import pytest from cloudformation_cli_python_lib.interface import HandlerErrorCode, OperationStatus +import importlib +import inspect + def get_public_exceptions(module_name="cloudformation_cli_python_lib.exceptions"): module = importlib.import_module(module_name) diff --git a/tests/lib/hook_test.py b/tests/lib/hook_test.py index 4b9520f..c8771fe 100644 --- a/tests/lib/hook_test.py +++ b/tests/lib/hook_test.py @@ -1,8 +1,5 @@ # pylint: disable=redefined-outer-name,protected-access,line-too-long -import json from dataclasses import dataclass -from datetime import datetime -from unittest.mock import Mock, call, patch, sentinel import pytest from cloudformation_cli_python_lib import Hook @@ -23,6 +20,10 @@ ) from cloudformation_cli_python_lib.utils import Credentials, HookInvocationRequest +import json +from datetime import datetime +from unittest.mock import Mock, call, patch, sentinel + ENTRYPOINT_PAYLOAD = { "awsAccountId": "123456789012", "clientRequestToken": "4b90a7e4-b790-456b-a937-0cfdfa211dfe", diff --git a/tests/lib/interface_test.py b/tests/lib/interface_test.py index 89b224c..3ab49fd 100644 --- a/tests/lib/interface_test.py +++ b/tests/lib/interface_test.py @@ -1,7 +1,5 @@ # pylint: disable=protected-access,redefined-outer-name,abstract-method -import json from dataclasses import dataclass -from string import ascii_letters import boto3 import pytest @@ -15,7 +13,9 @@ ) import hypothesis.strategies as s # pylint: disable=C0411 +import json from hypothesis import given # pylint: disable=C0411 +from string import ascii_letters @pytest.fixture(scope="module") diff --git a/tests/lib/log_delivery_test.py b/tests/lib/log_delivery_test.py index 91ab8aa..4087369 100644 --- a/tests/lib/log_delivery_test.py +++ b/tests/lib/log_delivery_test.py @@ -1,8 +1,4 @@ # pylint: disable=redefined-outer-name,protected-access -import logging -from unittest.mock import DEFAULT, Mock, create_autospec, patch -from uuid import uuid4 - import pytest from cloudformation_cli_python_lib.log_delivery import ( HookProviderLogHandler, @@ -18,6 +14,9 @@ import botocore.errorfactory import botocore.session +import logging +from unittest.mock import DEFAULT, Mock, create_autospec, patch +from uuid import uuid4 logs_model = botocore.session.get_session().get_service_model("logs") factory = botocore.errorfactory.ClientExceptionsFactory() diff --git a/tests/lib/metrics_test.py b/tests/lib/metrics_test.py index c5dbf57..7e62663 100644 --- a/tests/lib/metrics_test.py +++ b/tests/lib/metrics_test.py @@ -1,8 +1,5 @@ # auto enums `.name` causes no-member # pylint: disable=redefined-outer-name,no-member,protected-access -from datetime import datetime -from unittest.mock import Mock, call, patch - import pytest from cloudformation_cli_python_lib.interface import ( Action, @@ -19,6 +16,8 @@ import botocore.errorfactory import botocore.session +from datetime import datetime +from unittest.mock import Mock, call, patch cloudwatch_model = botocore.session.get_session().get_service_model("cloudwatch") factory = botocore.errorfactory.ClientExceptionsFactory() @@ -67,7 +66,7 @@ def test_put_metric_catches_error(mock_session): } with patch( - "cloudformation_cli_python_lib.metrics.LOG", auto_spec=True + "cloudformation_cli_python_lib.metrics.LOG", autospec=True ) as mock_logger: publisher.publish_metric( MetricTypes.HandlerInvocationCount, @@ -226,7 +225,7 @@ def test_put_hook_metric_catches_error(mock_session): } with patch( - "cloudformation_cli_python_lib.metrics.LOG", auto_spec=True + "cloudformation_cli_python_lib.metrics.LOG", autospec=True ) as mock_logger: publisher.publish_metric( MetricTypes.HandlerInvocationCount, diff --git a/tests/lib/recast_test.py b/tests/lib/recast_test.py index 3fc1634..b8933ff 100644 --- a/tests/lib/recast_test.py +++ b/tests/lib/recast_test.py @@ -1,7 +1,4 @@ # pylint: disable=protected-access -from typing import Awaitable, Generic, Optional, Union -from unittest.mock import patch - import pytest from cloudformation_cli_python_lib.exceptions import InvalidRequest from cloudformation_cli_python_lib.recast import ( @@ -12,6 +9,9 @@ recast_object, ) +from typing import Awaitable, Generic, Optional, Union +from unittest.mock import patch + from .sample_model import ResourceModel as ComplexResourceModel, SimpleResourceModel diff --git a/tests/lib/resource_test.py b/tests/lib/resource_test.py index f5cd94e..e8234a2 100644 --- a/tests/lib/resource_test.py +++ b/tests/lib/resource_test.py @@ -1,7 +1,5 @@ # pylint: disable=redefined-outer-name,protected-access from dataclasses import dataclass -from datetime import datetime -from unittest.mock import Mock, call, patch, sentinel import pytest from cloudformation_cli_python_lib.exceptions import InternalFailure, InvalidRequest @@ -15,6 +13,9 @@ from cloudformation_cli_python_lib.resource import Resource, _ensure_serialize from cloudformation_cli_python_lib.utils import Credentials, HandlerRequest +from datetime import datetime +from unittest.mock import Mock, call, patch, sentinel + ENTRYPOINT_PAYLOAD = { "awsAccountId": "123456789012", "bearerToken": "123456", diff --git a/tests/lib/sample_model.py b/tests/lib/sample_model.py index b3e43b4..aae36e9 100644 --- a/tests/lib/sample_model.py +++ b/tests/lib/sample_model.py @@ -2,8 +2,16 @@ # happening as expected # pylint: disable=invalid-name, too-many-instance-attributes, protected-access, abstract-method -import sys from dataclasses import dataclass + +from cloudformation_cli_python_lib.interface import ( + BaseModel, + BaseResourceHandlerRequest, +) +from cloudformation_cli_python_lib.recast import recast_object +from cloudformation_cli_python_lib.utils import deserialize_list + +import sys from inspect import getmembers, isclass from typing import ( AbstractSet, @@ -16,13 +24,6 @@ TypeVar, ) -from cloudformation_cli_python_lib.interface import ( - BaseModel, - BaseResourceHandlerRequest, -) -from cloudformation_cli_python_lib.recast import recast_object -from cloudformation_cli_python_lib.utils import deserialize_list - T = TypeVar("T") diff --git a/tests/lib/utils_test.py b/tests/lib/utils_test.py index 449e8fe..cf81cb9 100644 --- a/tests/lib/utils_test.py +++ b/tests/lib/utils_test.py @@ -1,7 +1,4 @@ # pylint: disable=protected-access -import json -from unittest.mock import Mock, call, sentinel - import pytest from cloudformation_cli_python_lib.exceptions import InvalidRequest from cloudformation_cli_python_lib.interface import BaseModel @@ -13,7 +10,9 @@ ) import hypothesis.strategies as s # pylint: disable=C0411 +import json from hypothesis import given # pylint: disable=C0411 +from unittest.mock import Mock, call, sentinel def roundtrip(value): diff --git a/tests/plugin/codegen_test.py b/tests/plugin/codegen_test.py index 68f993e..f4857ea 100644 --- a/tests/plugin/codegen_test.py +++ b/tests/plugin/codegen_test.py @@ -1,16 +1,10 @@ # pylint: disable=redefined-outer-name,protected-access -import ast -import importlib.util -from pathlib import Path -from shutil import copyfile -from subprocess import CalledProcessError -from unittest.mock import ANY, patch, sentinel -from uuid import uuid4 -from zipfile import ZipFile - import pytest +import ast +import importlib.util from docker.errors import APIError, ContainerError, ImageLoadError +from pathlib import Path from requests.exceptions import ConnectionError as RequestsConnectionError from rpdk.core.exceptions import DownstreamError from rpdk.core.project import Project @@ -21,6 +15,11 @@ Python36LanguagePlugin as PythonLanguagePlugin, validate_no, ) +from shutil import copyfile +from subprocess import CalledProcessError +from unittest.mock import ANY, patch, sentinel +from uuid import uuid4 +from zipfile import ZipFile TYPE_NAME = "foo::bar::baz" diff --git a/tests/plugin/parser_test.py b/tests/plugin/parser_test.py index 6a75d90..d058da5 100644 --- a/tests/plugin/parser_test.py +++ b/tests/plugin/parser_test.py @@ -1,5 +1,4 @@ import argparse - from rpdk.python.parser import setup_subparser_python36, setup_subparser_python37