-
Notifications
You must be signed in to change notification settings - Fork 245
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
🎯 Python Experience Improvements #1919
Comments
I know you're working on it already, but might want to put some form of "runtime type validation" in there as well. I saw a LOT of issues where users confused L1 and L2 classes. |
What you're writing about a better tool is correct, but is a big project. There's probably some effective short-term things we can do instead (for example: stop relying on
I'm missing some concrete examples here. Are you proposing to replace:
? |
Yes, exactly that. |
Did you recently change the method signatures to include line-breaks? That was my biggest pet peeve but it seems to be fixed now. |
I'd love to have a way to unit test my stacks. The assert module only works with TypeScript AFAIK. We now have to use snapshot tests. Ie. generate cloudformation template and compare it with a known and validated one. Then if the test fails, we get two huge strings that don't match. It works, but it's not atomic. |
Also fix the pip-installation: aws/aws-cdk#3406 (comment) |
@brainstorm can you elaborate a bit more on "fix"? Are you referring to one large package that includes all CDK modules instead of individual modules per service? |
Not instead, but in addition. Now, I know this could be seen as "bloat", but from a DX perspective, I reckon it's the better decision IMO? |
We're working on something similar with monocdk |
Adding a section to the documentation on unit testing with examples. It would be super slick to have this integrated with the cli, ie. cdk run-tests |
awkward experience when looking at docs indeed |
We used to run generated Python code through |
This is interesting but could be tricky (because we'd likely end up having to be opinionated with respects to test frameworks, and opinions usually don't get you a lot of new friends 🤣). We've considered this as "living with the languages' idioms", so we'd expect you want to do it "like you normally test". That seems to go against the idea of integrating this in the |
Improved the documentability of generated Python code by reducing the use of forward references through keeping track of types that were already emitted; and by emitting the kind of forward reference that `sphinx`' `autodoc` extension is able to resolve (forward references within a sub-package must be relative to that sub-package; and nested declarations can not use their surrounding type's name in forward references (those must be relative to the surrounding type's context). Additionally, removed duplicated headings that were generated for documentation segments (`see`, `returns`, ...), as those were causing redundant content to appear on the generated docsite. This completely removes `ForwardRef` instances from the Python reference documentation (as was checked by issuing a recursive `grep` on the generated documentation). Fixes #474 Related #1919 --- By submitting this pull request, I confirm that my contribution is made under the terms of the [Apache 2.0 license]. [Apache 2.0 license]: https://www.apache.org/licenses/LICENSE-2.0
I'd like to add some notes about my experience using CDK with type checking (specifically Pyright/Pylance in Visual Studio Code):
|
The interfaces should be declared as |
pyright is compaining about virtually all interfaces for me, is there a path to fixing this? |
I am also experiencing the interface issues described above. We're looking to adopt the CDK for our main platform and as a mostly Python/C++ shop the Python CDK seemed like a great choice for us. I didn't make it very far into my first sample app to test things out before mypy started throwing errors. from aws_cdk.core import Construct, Stack
from aws_cdk import aws_ec2 as ec2
class MyEC2Stack(Stack):
def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
super().__init__(scope, construct_id, **kwargs)
ec2.Instance(self, 'my-app',
instance_type=ec2.InstanceType('m5.4xlarge'),
machine_image=ec2.MachineImage.generic_linux({
'us-east-1': 'ami-0affd4508a5d2481b', # CentOS 7 (x86_64) - with Updates HVM
}),
vpc=ec2.Vpc(self, "my-app-vpc")) I would love to see this fixed so we can start building out our application stack with confidence that we're passing the right level of constructs around! |
Seeing this as well in multiple IDEs. I swear I didn't see it before, but maybe it was just ignored by my IDE before. Code certainly works, just need the right |
EDIT: Sorry I think this might actually be the wrong Issue I had bookmarked but I cannot for the life of me find the JSII / CDK issue discussing lack of intellisense and docstrings for CDK packages. -- I wanted to offer a cool feature I found in PyCharm, period, but also could be used to generate something better. In PyCharm: Add a new source as below:
Outcome
Speculation
|
I'm also having issues using Pyright and interfaces, same as @jarrodldavis described. |
Another quirk of the un-Pythonic I suspect there's some clever way of overloading |
About passing dicts: I am playing with an adjustment of https://github.com/aws/jsii/blob/main/packages/jsii-pacmak/lib/targets/python.ts In case where These setters could then cast in case a dict is found and the init would use those setters. This is the cast function: def cast(klass, value):
if value.__class__.__name__ == 'dict':
return klass(**value)
else:
return value For example CfnBucketProps from cdk: In init: if logging_configuration is not None:
self._values["logging_configuration"] = logging_configuration becomes: if logging_configuration is not None:
self.logging_configuration = logging_configuration And a setter is added: @logging_configuration.setter
def logging_configuration(
self,
value: typing.Optional[typing.Union["CfnBucket.LoggingConfigurationProperty", _IResolvable_da3f097b]],
) -> None:
self._values["logging_configuration"] = cast(CfnBucket.LoggingConfigurationProperty, value) This would allow those dicts :-) What do you think about this? |
@Jacco one property of jsii structs is that they are pure-data, immutable objects. Adding setters to those would break the immutable property. So basically, we'd need those setters to be |
Do you have an example of code that has this behavior? I'm a bit surprised here, as the metaclass doesn't directly override There might be an edge-case I'm not seeing and I'd like to step through some code that behaves incorrectly to get a sense of what's going on... |
Is this still a problem today? I have been trying to reproduce this (with CDK v2, though), and am not getting the MyPy error you report here... if you still happen to have the error, maybe this is because of your specific MyMy version or configuration (in which case, I would like to know what these are so I can reproduce). |
It would also be nice if interfaces/protocols could be runtime checkable. @jsii.implements(aws_cdk.IAspect)
class ConfigureConnections:
def visit(self, node):
# Raises error
# only protocols decorated with `@runtime_checkable` can be used for instance/subclass checks
if not isinstance(node, ec2.IConnectable):
return
# ...
Aspects.of(scope).add(ConfigureConnections(my_resources)) Is there a workaround for this shortcoming other than manually implementing protocol checking? |
For me this is the one thing that stops cdk8s with Python being widely usable. Is there any hope that it will be fixed? |
Indeed it is still a problem. For example try loading up apigw-http-api-lambda-dynamodb-python-cdk in vscode, then pyright reports for
typecheck error
as shown in this screenshot That project uses aws-cdk-lib==2.77.0 however the same issue exists after updating to recent aws-cdk-lib==2.143.1. On my machine it happens with the following configuration
|
Preamble
This is a tracking issue summarizing the existing pain points in Python front-ends generated by
jsii-pacmak
. Members of the community are more than welcome to contribute to this, for example by sharing their pet peeves in the communication thread below.The initial post will be periodically updated to include a summary of the items that are accepted as needed improvements to our Python offering.
Known Pain Points
Contributing.md
for pulling the superchain docker image don't work aws-cdk#474 - Incomplete CDK documentation due to quirks insphinx
Forwardref
jsii-rosetta
does not require that examples compiledicts
in lieu of jsii structs does not consistently work@jsii/kernel
are obscureDiscussion
Incomplete CDK Documentation
The AWS CDK API reference documentation is currently generated using a private tool, which leverages the idiomatic documentation generator for each supported language (
docfx
,javadoc
,sphinx
, ...). While this has the advantage of giving users the same documentation experience that they're used to for their language of choice, it has several disadvantages:sphinx
and nested types)Additionally, the current generator being private means other users of
jsii
are not able to produce a documentation site that looks and feels like the AWS CDK reference. One of the goals ofjsii
being that developers need not learn all about the standard tools of front-end languages, providing ajsiidoc
tool would greatly reduce friction around documenting polyglot APIs. This is however a significant project, and shorter-term solutions (such as usingautodoc
for Python with generated.rst
files).Incorrectly Transliterated Sample Code
The
jsii-rosetta
tool is able to collect code example snippets from various locations in thejsii
assemblies, and attempts to automatically transliterate those to other front-end languages. It delivers the best fidelity when it is able to obtain complete type information around the example (i.e: it is valid and compiling TypeScript code); and must resort to broad assumptions otherwise.In the context of AWS CDK, many example snippets lack context information necessary in order to obtain complete type information, which often leads to incorrectly transliterated code. In the Python context, this can mean the transliterated code uses keyword arguments at a location where they are not actually supported.
There is a
--fail
option tojsii-rosetta
which will cause it to abort if an example is not compiling (the corollary of this being there cannot be sufficient type information). Enabling this behavior has two benefits:This feature is however not enabled by default on the AWS CDK, as many examples there do not have the full context needed for them to compile. In many cases, all that is needed is the introduction of fixtures that provide the necessary context while not weighting example code shown in the documentations with repetitive boilerplate.
Un-pythonic reliance on a custom metaclass
The jsii runtime for Python provides a
JSIIMeta
metaclass that is used by all generated bindings for classes (classes from the jsii assembly). As a consequence, attempting to leverage Python's multiple inheritance (for example, to implement an interface explicitly) may lead to a runtime error, as all bases of a Python class must be rooted on the same metaclass.Additionally, the
JSIIMeta
metaclass is merely used to record jsii-specific metadata on generated classes; but since those are code-generated, it would be trivial to register the exact same metadata as part of the generated declarations, rendering the metaclass useless.Not relying on the metaclass would mean users are able to implement interfaces explicitly in the idiomatic way, which would provide better information to static analyzers such as
mypy
, to help ensure developers have correctly implemented an interface, and are correctly passing values that conform to interfaces to method parameters.Passing
dict
in lieu of jsii structs does not consistently workSimilar to the Javascript idioms, Python developers will often intuitively use
dict
literals in places where the API expects some instance of a jsii struct. The generated Python code has specific code paths to convert adict
to the correct struct instance, however there seem to be many cases where this conversion is not happening, leading to:@jsii/kernel
modulesnake_case
tocamelCase
@jsii/kernel
The workaround forces users to resort to relatively verbose boilerplate code, which in turns looks very un-pythonic.
Type-checking errors from
@jsii/kernel
are obscureWhen a user makes a programming mistake and passes the wrong kind of value to an API (be it a parameter of some method or constructor, or the value being set to some property), the errors generated by the
@jsii/kernel
are not very helpful to developers as:The text was updated successfully, but these errors were encountered: