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

Validate unparsed attributes and subentities in launch_xml and launch_yaml #468

Merged
merged 9 commits into from
Nov 4, 2020

Conversation

ivanpauno
Copy link
Member

Fixes #338.

Adds three methods to launch.frontend.Entity:

  • assert_no_children(self)
  • assert_subentity_types(self, types: Iterable[str])
  • assert_attribute_names(self, names: Iterable[str])

It might make the parse methods a bit more verbose, but it's not hard to use.
I have modified two parsing functions to use this, and it has already caught a preexisting error!

There might be some other ways to achieve the same, so I would like to get feedback about the approach before applying this more broadly.

…_yaml

Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
@ivanpauno ivanpauno added the enhancement New feature or request label Oct 29, 2020
@ivanpauno ivanpauno self-assigned this Oct 29, 2020
Copy link
Member

@jacobperron jacobperron left a comment

Choose a reason for hiding this comment

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

I think it would be nicer to have children and attributes explicitly declared versus calling separate assert functions. For example, by extending the expose_action decorator:

@expose_action(
    'executable',
    allowed_attributes=(
        'cmd',
        'cwd',
        'name',
        'launch-prefix',
        'output',
        'respawn',
        'respawn_delay',
        'shell',
        'additional_env',
        'if',
        'unless'
    ),  
    allowed_children=('env', ))
class ExecuteProcess(Action):
    ...

If allowed_* is empty (or None), then implicitly none are allowed. This way we'll expose bugs in user launch scripts right away; the downside being that we'll break a lot of existing user code, but maybe we could tick-tock the feature, emitting a warning if they haven't explictly declared attributes/children they are accessing.

It might also be nice to have a flag to allow any attributes or children. I'm not sure what that would look like.

I don't know the details of the frontend implementation, so I'm not sure how feasible what I proposed is.

@ivanpauno
Copy link
Member Author

ivanpauno commented Oct 30, 2020

I think it would be nicer to have children and attributes explicitly declared versus calling separate assert functions. For example, by extending the expose_action decorator:

I like that one.
The assert functions in launch.frontend.Entity are needed, but those can be called directly from the description in the decorator.

I will still need to distinguish between allow_subentities (e.g. env in ExecuteProcess), and allow children (e.g. any action in GroupAction).

Copy link
Contributor

@hidmic hidmic left a comment

Choose a reason for hiding this comment

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

First of all, to put things in context, I think that, to solve #338, we have two problems at hand: (1.) to enforce an schema as we parse, and (2.) to correlate any errors with the given input. To complain about a bad tag somewhere in, say, a three levels deep launch file hierarchy is of little use.

Now, this patch addresses the first problem. I agree with @jacobperron's suggestion, but based on my observations, @ivanpauno I (personally) think it's time to go back to that old premise about frontends being standardized 1-to-1 mappings to their Python implementation.

I'd suggest we use the full power of Python's introspection machinery from within our decorators to deduce which attributes and children are valid from __init__ arguments (along with their annotated types) for the decorated class and its base classes, recursively. As a bonus, we get to enforce that 1-to-1 mapping.

if ignore is None:
entity.assert_attribute_names((
'cmd', 'cwd', 'name', 'launch-prefix', 'output', 'respawn', 'respawn_delay', 'shell',
'additional_env', 'if', 'unless'
Copy link
Contributor

Choose a reason for hiding this comment

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

@ivanpauno hmm, do we have to explicitly list base classes' attributes? What's going to happen with the assertions performed by another class that derives from this one?

Copy link
Member Author

Choose a reason for hiding this comment

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

That's why I used the trick if ignore is None:.
Not a beauty, but I don't know how to do better.

launch_xml/launch_xml/entity.py Outdated Show resolved Hide resolved
@ivanpauno
Copy link
Member Author

I (personally) think it's time to go back to that old premise about frontends being standardized 1-to-1 mappings to their Python implementation.

I'd suggest we use the full power of Python's introspection machinery from within our decorators to deduce which attributes and children are valid from init arguments (along with their annotated types) for the decorated class and its base classes, recursively. As a bonus, we get to enforce that 1-to-1 mapping.

I don't disagree, but we aren't mapping them 1-to-1 right now, and start doing that would be a breaking change.
In the case of a class with base classes the mechanism isn't obvious (e.g. node accepts different arguments that executable, but some are directly forwarded to the base class).
In that last case, the decorator will still need some user input (a solution to this is to never use kwargs, and always forward arguments explicitly in constructors).

We could still add a more intelligent mechanism that deduce the attribute names from the constructor arguments in the future, I think this improvement wouldn't block that (specially if following @jacobperron suggestion of providing the valid attributes and sub-entities in the decorator).

@ivanpauno
Copy link
Member Author

It's also not obvious how the decorator would guess what's an attribute (name, executable, plugin, etc), what are children subentities (nested actions in group_action) and what are complex attributes (env, param, etc). Maybe there could be some heuristic based on type annotations, but I'm not sure if that will always work,

@hidmic
Copy link
Contributor

hidmic commented Oct 30, 2020

I don't disagree, but we aren't mapping them 1-to-1 right now, and start doing that would be a breaking change.

Hmm, aren't we? IIRC that's what we strived for. I'd suggest we don't delay in correcting the deviation. User provided aliases can aid deprecation cycles.

We could still add a more intelligent mechanism that deduce the attribute names from the constructor arguments in the future, I think this improvement wouldn't block that

I agree schema deduction is not strictly necessary. To be clear, I meant it as a proposal to solve the problem. I'm sure there are other solutions, perhaps better ones. We can discuss them. But that is not to say I agree with this patch being an improvement as long as it requires replicating information across inheritance chains like this.

In the case of a class with base classes the mechanism isn't obvious (e.g. node accepts different arguments that executable, but some are directly forwarded to the base class).

One solution to that problem (and perhaps an alternative, explicit, solution to the problem w/o using introspection) is to progressively tag input (at least for validation purposes) as it is accessed. If by the time you've processed an entire subtree, a part of it remains unacknowledged, then you know the system didn't recognize it.

the decorator will still need some user input

This is correct. The canonical example being arbitrary children mapped to an specific argument. I'm not against user input, but against introducing unnecessary coupling in the system.

It's also not obvious how the decorator would guess what's an attribute (name, executable, plugin, etc), what are children subentities (nested actions in group_action) and what are complex attributes (env, param, etc). Maybe there could be some heuristic based on type annotations, but I'm not sure if that will always work,

Once it is clear which (and if an) argument maps to all nested entities (which IIRC cannot coexist with complex attributes), you can deduce attributes at first depth from type annotations. Of course, it cannot deduce the structure of complex attributes -- whose shape may be the result of a parsing function's implementation e.g. nested dictionaries.

@ivanpauno
Copy link
Member Author

One solution to that problem (and perhaps an alternative, explicit, solution to the problem w/o using introspection) is to progressively tag input (at least for validation purposes) as it is accessed. If by the time you've processed an entire subtree, a part of it remains unacknowledged, then you know the system didn't recognize it.

Yeah, I thought about that one.
I will try to implement something like that and see how it works.

…of them were parsed

Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
@ivanpauno
Copy link
Member Author

I have updated the PR with the approach suggested above, good news is that we don't need to update any parsing function.

Copy link
Member

@jacobperron jacobperron left a comment

Choose a reason for hiding this comment

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

Generally looks good. I've left some minor comments

launch/launch/frontend/expose.py Outdated Show resolved Hide resolved
launch_xml/launch_xml/entity.py Outdated Show resolved Hide resolved
launch_xml/launch_xml/entity.py Outdated Show resolved Hide resolved
launch_yaml/launch_yaml/entity.py Outdated Show resolved Hide resolved
launch/launch/frontend/expose.py Show resolved Hide resolved
ivanpauno and others added 3 commits November 2, 2020 14:57
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>

Co-authored-by: Jacob Perron <jacob@openrobotics.org>
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
Copy link
Member

@jacobperron jacobperron left a comment

Choose a reason for hiding this comment

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

LGTM. It'd be good to have @hidmic take a look too.

@ivanpauno
Copy link
Member Author

CI, testing all downstream packages of launch:

  • Linux Build Status
  • Linux-aarch64 Build Status
  • macOS Build Status
  • Windows Build Status

launch/launch/frontend/entity.py Outdated Show resolved Hide resolved
launch/launch/frontend/expose.py Show resolved Hide resolved
launch_xml/launch_xml/entity.py Show resolved Hide resolved
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
Copy link
Contributor

@hidmic hidmic left a comment

Choose a reason for hiding this comment

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

LGTM pending green CI!

launch_xml/test/launch_xml/test_arg.py Show resolved Hide resolved
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
@ivanpauno
Copy link
Member Author

ivanpauno commented Nov 3, 2020

  • Linux Build Status
  • Linux-aarch64 Build Status
  • macOS Build Status
  • Windows Build Status

@adityapande-1995
Copy link
Contributor

@Mergifyio backport foxy

mergify bot pushed a commit that referenced this pull request Dec 8, 2021
…_yaml (#468)

Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
(cherry picked from commit 00f7ccf)
@mergify
Copy link

mergify bot commented Dec 8, 2021

backport foxy

✅ Backports have been created

adityapande-1995 pushed a commit that referenced this pull request Dec 22, 2021
…_yaml (#468) (#567)

Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
(cherry picked from commit 00f7ccf)

Co-authored-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
jacobperron added a commit that referenced this pull request Feb 22, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[launch_frontend] Produce errors for invalid entities and attributes
4 participants