Skip to content
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

Introduce new style for python extensions #3302

Merged
merged 26 commits into from
Sep 13, 2023
Merged

Introduce new style for python extensions #3302

merged 26 commits into from
Sep 13, 2023

Conversation

jleibs
Copy link
Member

@jleibs jleibs commented Sep 12, 2023

What

Python extensions can now be implemented by declaring a class TypeExt in an adjacent file type_ext.py.

This matches the existing conventions established in the cpp and rs code-generators.

This import is done automatically as a relative import from the base type, so no more maintaining overrides __init__.py import-lists for extensions. Additionally, the extension type itself provides the scope for all exposed extensions, simplifying the generated import.

init() is now overridden by declaring __init__ directly on the extension class. This is a departure from declaring init_override but means things like help and code completion for the class now see the actual init implementation. For example:

> help(rr2.dt.Angle)

Help on class Angle in module rerun._rerun2.datatypes.angle:

class Angle(rerun._rerun2.datatypes.angle_ext.AngleExt)
|  Angle(rad: 'float | None' = None, deg: 'float | None' = None) -> 'None'

Field converters no longer need the classname prefix. color__rgba__field_converter_override -> ColorExt.rgba__field_converter_override.

Any other methods on the extension class are found through normal python function dispatch.

Resolves: #3268

The PR only migrates Color and Angle as a proof-of-concept. Additional types will be migrated in follow-up PRs and then support for the previous override mechanism can be removed.

Checklist

@jleibs jleibs marked this pull request as ready for review September 12, 2023 20:06
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the main file to review.

@jleibs jleibs added codegen/idl 🐍 Python API Python logging API labels Sep 12, 2023
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Example of overriding init.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Example of overriding field converters and providing a native_to_pa_array_override.

Copy link
Member

@emilk emilk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OMG I LOVE IT

crates/re_types_builder/src/codegen/python.rs Outdated Show resolved Hide resolved
crates/re_types_builder/src/codegen/python.rs Outdated Show resolved Hide resolved
crates/re_types_builder/src/codegen/python.rs Show resolved Hide resolved
Comment on lines +550 to +551
let init_define_arg = if ext_class.has_init || overrides.contains(&old_init_override_name) {
"init=False".to_owned()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if has_init, then init=False… I don't follow

Copy link
Member Author

@jleibs jleibs Sep 13, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding comment to clarify:

        // If the `ExtensionClass` has its own `__init__` then we need to pass the `init=False` argument
        // to the `@define` decorator, to prevent it from generating its own `__init__`, which would
        // take precedence over the `ExtensionClass`.

crates/re_types_builder/src/codegen/python.rs Show resolved Hide resolved
format!(
"# You can define your own __init__ function as a member of {} in {}",
ext_class.name, ext_class.file_name
),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🙏

Comment on lines +693 to +695
quote_array_method_from_obj(ext_class, overrides, objects, obj),
1,
4,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

off-topic, but I find the 1, 4, really confusing and hard to read.

To me it would be more natural with the argument-order indentation, text, newlines, since that is the order they will be emitted in the code.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, after working in this code I want to build a clearer helper function / macro.

rerun_py/ARCHITECTURE.md Outdated Show resolved Hide resolved
rerun_py/rerun_sdk/rerun/_rerun2/datatypes/color_ext.py Outdated Show resolved Hide resolved
@Wumpf
Copy link
Member

Wumpf commented Sep 13, 2023

we have all extension mechanisms documented in re_types/src/lib.rs, please update the doc string there

@jleibs jleibs merged commit 6c052de into main Sep 13, 2023
26 checks passed
@jleibs jleibs deleted the jleibs/python_ext branch September 13, 2023 13:22
emilk pushed a commit that referenced this pull request Sep 13, 2023
### What

Builds on top of: #3302

One of the items raised by #3268
was wanting to extend a component type. But components previously had no
instantiation.

This PR creates Component objects that derive from their baseclass as
well as the extension class. These can be used anywhere the datatype
could be used instead.

Example with TextLogLevel:
```
>>> rr2.TextLog(body="Hello", level=rr2.cmp.TextLogLevel.WARN)
rr.TextLog(
  rerun.label<string>(
    ['Hello']
  )
  rerun.components.TextLogLevel<string>(
    ['WARN']
  )
  rerun.colorrgba<uint32>(
    []
  )
)

>>> rr2.TextLog(body="World", level=rr2.cmp.TextLogLevel("my custom level"))
rr.TextLog(
  rerun.label<string>(
    ['World']
  )
  rerun.components.TextLogLevel<string>(
    ['my custom level']
  )
  rerun.colorrgba<uint32>(
    []
  )
)
```

### Checklist
* [x] I have read and agree to [Contributor
Guide](https://github.com/rerun-io/rerun/blob/main/CONTRIBUTING.md) and
the [Code of
Conduct](https://github.com/rerun-io/rerun/blob/main/CODE_OF_CONDUCT.md)
* [x] I've included a screenshot or gif (if applicable)
* [x] I have tested [demo.rerun.io](https://demo.rerun.io/pr/3303) (if
applicable)

- [PR Build Summary](https://build.rerun.io/pr/3303)
- [Docs
preview](https://rerun.io/preview/177f58319e85da35d223c1fd0a109d35fbc7bd03/docs)
<!--DOCS-PREVIEW-->
- [Examples
preview](https://rerun.io/preview/177f58319e85da35d223c1fd0a109d35fbc7bd03/examples)
<!--EXAMPLES-PREVIEW-->
- [Recent benchmark results](https://ref.rerun.io/dev/bench/)
- [Wasm size tracking](https://ref.rerun.io/dev/sizes/)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add _ext.py for Python codegen
3 participants