Skip to content

🎯 Python Experience Improvements #1919

@RomainMuller

Description

@RomainMuller

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

  • Instructions in Contributing.md for pulling the superchain docker image don't work aws-cdk#474 - Incomplete CDK documentation due to quirks in sphinx
    • Nested types are missing
    • Many parameters are showing typed as Forwardref
  • Incorrectly transliterated sample code in the cdk documentation
    • Type information is necessary in many cases, and jsii-rosetta does not require that examples compile
    • In many cases, it's only a matter of adding a rosetta fixture
  • Un-pythonic reliance on a custom metaclass
    • Metaclasses complicate extension: all sub-classes in a class family must eventually derive from the same metaclass
    • Python doesn't really have interfaces, the idiom is using Protocols
      • Protocols can be implicitly implemented (structural typing)
      • Explicit implementation uses the extension syntax, but this cannot work due to metaclass dissonance
  • Passing dicts in lieu of jsii structs does not consistently work
    • Case adjustments of values is not always consistently done, resulting in runtime errors or worse (silently ignored properties, incorrect/unexpected side-effects, ...)
  • Type-checking errors from @jsii/kernel are obscure

Discussion

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:

  • it is an awkward experience for users building polyglot applications, since each language's documentation set is entirely separate from the others
  • we have to suffer the limitations of the idiomatic tools
    • customizing templates is often a difficult process
    • certain jsii features are broken or weird in some languages (i.e: 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 of jsii being that developers need not learn all about the standard tools of front-end languages, providing a jsiidoc tool would greatly reduce friction around documenting polyglot APIs. This is however a significant project, and shorter-term solutions (such as using autodoc 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 the jsii 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 to jsii-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:

  • It guarantees example code is up-to-date with the current library's API
  • Complete type information should be available, enabling the best possible transliteration result

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.

# The slightly awkward:
@jsii.implements(lib.IInterface)
class Implementor():
    ...

# Could become:
class Implementor(lib.IInterface):
    ...

Passing dict in lieu of jsii structs does not consistently work

Similar 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 a dict to the correct struct instance, however there seem to be many cases where this conversion is not happening, leading to:

  • type-checking errors in the @jsii/kernel module
  • property keys not being translated from snake_case to camelCase
  • properties being dropped entirely by the @jsii/kernel

The workaround forces users to resort to relatively verbose boilerplate code, which in turns looks very un-pythonic.

# The somewhat heavy-handed
s3.Bucket(self, 'Gold', lifecycle_rules=[s3.LifecycleRule(expiration=cdk.Duration.days(15)])

# Could become:
s3.Bucket(self, 'Gold', lifecycle_rules=[{ 'expiration': cdk.Duration.days(15) }])

Type-checking errors from @jsii/kernel are obscure

When 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:

  • They no not relate to the user's native language type names
  • They may lack sufficient context to be parseable by the user (for example, when the error occurs in a deeply-nested value)

Metadata

Metadata

Assignees

No one assigned

    Labels

    effort/largeLarge work item – several weeks of effortfeature-requestA feature should be added or improved.language/pythonRelated to Python bindingsmanagement/trackingIssues that track a subject or multiple issuesp1

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions