jsii: MyPy throwing type errors when it shouldn't

In the Python CDK, attempting to pass aws_events_targets.LambdaFunction as the target of an events.Rule fails to typecheck:

$ mypy test.py
test.py:19: error: List item 0 has incompatible type "LambdaFunction"; expected "IRuleTarget"
test.py:19: note: 'LambdaFunction' is missing following 'IRuleTarget' protocol member:
test.py:19: note:     __jsii_proxy_class__
Found 1 error in 1 file (checked 1 source file)

It does run perfectly fine, however – this is purely an error in the typesystem.

Reproduction Steps

from aws_cdk import aws_events, aws_events_targets, aws_lambda, core

class Example(core.Construct):
    def __init__(self, scope: core.Construct):
        super().__init__(scope, "some-name")
        some_lambda = aws_lambda.Function(
            self,
            "some-handler",
            runtime=aws_lambda.Runtime.PYTHON_3_8,
            code=aws_lambda.AssetCode("some/path"),
            handler="some_package.handler",
            retry_attempts=0,
        )
        aws_events.Rule(
            self,
            "some-rule",
            schedule=aws_events.Schedule.cron(minute="*/5"),
            targets=[aws_events_targets.LambdaFunction(some_lambda)],
        )

pip install mypy && mypy test.py

Error Log

test.py:19: note: 'LambdaFunction' is missing following 'IRuleTarget' protocol member:
test.py:19: note:     __jsii_proxy_class__

Environment

  • CLI Version: 1.17.13
  • Framework Version: 1.31.0
  • OS: OS X 10.14.6
  • Language: Python 3.8.0 / mypy 0.770

Other

It is also possible this is a bug in mypy.


Possible duplicate: https://github.com/aws/jsii/issues/1262


This is 🐛 Bug Report

About this issue

  • Original URL
  • State: open
  • Created 4 years ago
  • Reactions: 18
  • Comments: 15 (5 by maintainers)

Most upvoted comments

I’m running into the same issue with aws_cloudwatch.Metric not correctly implementing IMetric. Here’s a snippet that demonstrates the issue:

import aws_cdk.aws_cloudwatch as cloudwatch
import aws_cdk.aws_kinesis as kds
from aws_cdk import core


class MyStack(core.Stack):
    def __init__(self, scope: core.Construct, _id: str, **kwargs) -> None:
        super().__init__(scope, _id, **kwargs)

        self.template_options.description = "Demo"

        stream = kds.Stream(self, "InputStream", shard_count=16)

        dashboard = cloudwatch.Dashboard(self, "Dashboard", dashboard_name=core.Aws.STACK_NAME)

        incoming_records = cloudwatch.Metric(
            namespace="AWS/Kinesis",
            metric_name="IncomingRecords",
            dimensions={"StreamName": stream.stream_name},
            period=core.Duration.minutes(1),
            statistic="sum",
        )

        dashboard.add_widgets(
            cloudwatch.GraphWidget(
                left=[incoming_records],  # 'Metric' is missing following 'IMetric' protocol member
                width=24,
                title="Kinesis data stream (incoming)",
                left_y_axis=cloudwatch.YAxisProps(min=0),
                right_y_axis=cloudwatch.YAxisProps(min=0),
            )
        )

Checking types with Mypy results in the following error:

'Metric' is missing following 'IMetric' protocol member:
__jsii_proxy_class__
List item 0 has incompatible type "Metric"; expected "IMetric"

Sorry for not making clear that yeah, this is not about the functionality (which works great!), but about the type-correctness of the jsii-generated typing of the code.

I’ve found two other ones: neither aws_cloudwatch.MathExpression nor aws_cloudwatch.Metric implement IMetric as they are documented to, also because they don’t implement __jsii_proxy_class__.

@MrArnoldPalmer @RomainMuller Running into this error as well.

@MrArnoldPalmer @RomainMuller any updates on this? All interfaces have this error for me.

We recently had MyPy start throwing some new type errors that previously it wasn’t. Its likely that previously it wasn’t catching this missing implementation and this was fixed in a recent release of MyPy. We can investigate this on the JSII side but its likely that the python bindings are missing MyPy annotations in places. We will have to figure out why this isn’t caught during build time as well.

Please re-read the code I posted. I am using the aws_cdk.aws_events_targets.LambdaFunction class. The bug is that LambdaFunction does not implement IRuleTarget as it is documented to, because it’s missing a __jsii_proxy_class__ method.

Which appears to be accurate. Looking at this more closely, IRuleTarget is a protocol which defines the non-abstract static method __jsii_proxy_class__, as well as bind. Because both are part of the protocol’s definition, it means that LambdaFunction must itself define a static __jsii_proxy_class__ method to be an IRuleTarget, which it doesn’t currently:

$ python
Python 3.8.0 (default, Mar 18 2020, 12:21:22)
[Clang 10.0.1 (clang-1001.0.46.4)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from aws_cdk import aws_events, aws_events_targets
>>> aws_events_targets.LambdaFunction.__jsii_proxy_class__()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: type object 'LambdaFunction' has no attribute '__jsii_proxy_class__'