Skip to content

Proposal: change how generated modules are named and nested by default #218

@dicej

Description

@dicej

Currently, if you tell componentize-py to target a world that imports the interface foo:bar/baz@0.1.0, it will generate a Python module with one of the following names by default, depending on what other interfaces are included in that world:

  • wit_world.imports.baz if there are no other interfaces named baz referenced by the world
  • wit_world.imports.foo_bar_baz if there is at least one other interface named baz (e.g. x:y/baz@0.4.0) referenced by the world, but none in the same foo:bar package
  • wit_world_imports.foo_bar_baz_0_1_0 if there is at least one other interface named baz in the foo:bar package with a different version (e.g. foo:bar/baz@0.2.0)

In other words, the generated module will be given a short name if unambiguous, but a longer, more specific name otherwise. The advantage of this approach is that it's concise for simple cases. The main disadvantage is that, if the world is changed later to add new (versions of) interfaces, previously-unambiguous names may become ambiguous, requiring changes even for code which isn't using the new interfaces. That's especially painful for library packages, turning semver-compatible, purely additive WIT changes into semver-breaking library changes. It also creates a name clash hazard if code is generated for multiple worlds using multiple componentize-py bindings commands.

Considering similar tools for other languages:

  • ComponentizeJS always generates full names for modules, thereby avoiding such issues.
  • componentize-go generates mostly full names, always including the namespace and package name, but omits the version number if unambiguous unless the --include-versions option is specified, so it still has the same hazard, although only with respect to versions, not package names.
  • The Rust wit-bindgen nests the interface module in namespace and package modules, but omits version numbers if unambiguous, similar to componentize-go.

Personally, I think ComponentizeJS makes the right choice here, and we should update the tools for other languages to match. In the case of componentize-py, that would mean making the --full-names option the default and either adding a --short-names option to turn it off, or not support turning it off (but still allow overriding the names on a case-by-case basis using the --import-interface-name and --export-interface-name options).

In addition, I'd like to propose that instead of placing the modules generated for each interface under wit_world.imports.* by default, make them top-level modules. That would more closely match the ComponentizeJS experience, where you use e.g. import "foo:bar/baz@0.1.0". In Python, we'd do import foo_bar_baz_0_1_0 (or import foo_bar_baz_0_1_0 as baz if desired). We'd still use wit_world for world-level types and functions, but stop using it for interface-level ones. Also, we'd still put code generated for exports under an exports (bikeshed-able) module to avoid clashes when the same interface is both imported and exported.

For example, consider the following WIT world:

package foo:bar@0.1.0;

interface baz {
  wow: func();
}

world my-world {
  import baz;
  export baz;
  import yay: func();
}

Today componentize-py bindings would generate the following modules by default:

  • wit_world.exports: contains a Baz abstract base class and baz submodule representing the exported interface
  • wit_world.imports.baz: contains the wow function from the imported interface
  • wit_world: contains the yay function imported at the world level

I'm proposing that changes to:

  • exports: contains a FooBarBaz010 abstract base class and foo_bar_baz_0_1_0 submodule representing the exported interface
  • foo_bar_baz_0_1_0: contains the wow function from the imported interface
  • wit_world: contains the yay function imported at the world level

Thoughts? Opinions? Bikeshed colors?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions