-
Notifications
You must be signed in to change notification settings - Fork 142
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
Add frontend module in launch, launch_xml and launch_yaml packages #226
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ivanpauno alright, looking good, thanks for pushing this!
c46b260
to
0d2e6ac
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Second pass, sorry for the delay to review :(
992f531
to
b28736f
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wow, that's a large PR but I have to say it really honors the design document. It looks amazing, great job man!
```python | ||
'int', 'float', 'bool', 'list[int]', 'list[float]', 'list[bool]', 'list[str]', 'str' | ||
``` | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ivanpauno meta: IMHO, type deduction documentation is design document material.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you want, I can update ros2/design#208 and ros2/design#207, and create a new PR for yaml design doc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Eventually, yeah, but let's hold that for a minute. At least until we've settled here.
I fully agree and also think we need to devise a way to reuse parsing code in derived actions and substitutions. |
Yeah, that is tricky. I'll try to take a stab at it at some point, should be possible though. |
400e070
to
2100794
Compare
def test_combining_quotes_nested_substitution(): | ||
subst = default_parse_substitution( | ||
'$(env "asd_bsd_qsd_$(test \'asd_bds\')" \'$(env DEFAULT)_qsd\')' | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@hidmic I added this new test case. For having a more uniform behavior, I think this should look like:
'$(env "asd_bsd_qsd_$(test \\\'asd_bds\\\')" \'$(env DEFAULT)_qsd\')'
(this is not a raw a string, I'm escaping both the \ and ')
For that, I would change the following in the grammar:
SINGLE_QUOTED_STRING: (/[^'\\"$]|\$(?=!\()|(?<=\\)\$/ | "\\\"" | "\\'" | "\\")+
SINGLE_QUOTED_RSTRING: (/[^ '\\"$\(\)]|(?<=\\)\(|(?<=\\)\)|\$(?=!\()|(?<=\\)\$/ | "\\\"" | "\\'" | "\\")+
DOUBLE_QUOTED_STRING: (/[^"\\'$]|\$(?=!\()|(?<=\\)\$/ | "\\\"" | "\\'" | "\\")+
DOUBLE_QUOTED_RSTRING: (/[^ "\\'$\(\)]|(?<=\\)\(|(?<=\\)\)|\$(?=!\()|(?<=\\)\$/ | "\\\"" | "\\'" | "\\")+
Basically, I'm stopping allowing any kind of quote inside SINGLE_QUOTED_*
and DOUBLE_QUOTED_*
, and only allowing it if it is escaped.
Do you agree with that change?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm fine with it, though it may be counter intuitive for some accustomed to combine quotes. If we do it, we should clearly document it somewhere. @wjwwood any thoughts on this i.e. on forcing quote escaping everywhere if one wants to keep it raw?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Escaping quotes inside of quotes is ok by me.
5e1a0b4
to
9c3a9f6
Compare
Rebased with master again. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ivanpauno I think we need to polish overall documentation format a bit too.
- 'list[Entity]' | ||
|
||
'guess' work in the same way as: | ||
('int', 'float', 'bool', 'list[int]', 'list[float]', 'list[bool]', 'list[str]', 'str') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ivanpauno why not replace 'guess'
by the absence of a type and make it the default?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because the type that is specified most of the times is str
(substitutions are str
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same, making str
the default.
it will check if the attribute read match with one in `types`. | ||
If it is one of them, the value is returned. | ||
If not, an `TypeError` is raised. | ||
- For frontends that don't natively recognize data types (like xml), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ivanpauno hmm, these two bullets are packed with assumptions about downstream implementations and markup languages that I'd prefer to spare. Consider just stating that the types
argument states the expected types of a given attribute and whether that results in type coercion or type checking depends on the particular frontend.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, now that I think of it, it might make sense to let the user choose between checks and coercions.
return default_parse_substitution(value) | ||
|
||
@classmethod | ||
def parse_description(cls, entity: Entity) -> launch.LaunchDescription: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ivanpauno hmm, I'm somewhat inclined to say that these methods don't need nor should be class methods.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
parse_description
, parse_action
should. cls
is finally passed to the action parsing methods as parser
argument. The parsing methods could call parse.substitution
, which can be overridden by the derived class.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, my point being that these should be instance methods.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Parse
instances don't have a state now, but it could have it in the future, so I think it is a good idea. I will change parse_action
, parse_substitution
, parse_description
to instance methods.
load
, load_parser_extensions
and load_parser_implementations
will be continue being class methods.
I will stop returning an object of Parse
type in the load
method (ie: a subclass of Parse
) , and start returning an instance of it.
Note: I will also rename load_parser_extensions
as load_launch_extensions
.
@@ -0,0 +1,155 @@ | |||
# Copyright 2019 Open Source Robotics Foundation, Inc. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ivanpauno after going over this file a couple times, I think that using strings for type identification is a bit clunky. We could use type objects as well, which would then simplify the implementation. Thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem is with 'list[int]'
, etc. Using typing
objects doesn't help much in the implementation (i.e.: I don't have an easy way to extract if it was a list, and what is the item type).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, but you can check for equality just like you do with strings. And then you don't have to do substring checks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still wonder if it wouldn't be better to use typing
objects instead of plain str
ings.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't have a strong opinion. Using typing
would be cool, but I don't know if it makes it faster or cleaner.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, between using typing
classes and using a stringification of their names, I'm slightly inclined towards the first one. Collection type information is readily available and the API looks cleaner that way IMO. No need to do substring checks anymore.
"""Load parser extension, in order to get all the exposed substitutions and actions.""" | ||
if cls.extensions_loaded is False: | ||
for entry_point in iter_entry_points('launch_frontend.parser_extension'): | ||
entry_point.load() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ivanpauno meta: we can leave it as is for a first iteration, but I'm not entirely convinced that working with importation side effects is in our best interest in the long run. I wonder if having some sort of index at each package root module that its classes can expose/register themselves into would be cleaner than a having a global one. Maybe @wjwwood can weigh in too on this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you talking about using pkg_resources
or something else (sorry I didn't really grok "importation side effects" in this context)?
pkg_resources
is known to be a bit slow, but this should be a one time activity. Of course, we don't want launch startup to be slow either, so I don't have a strong feeling one way or the other. We talked about other solutions in catkin tools, and some guy actually commented that they made a replacement system:
We could use ament index instead, but I don't have any strong feelings about it one way or the other.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not so much about performance (though I had not event considered that, thanks for the reference) but the way actions, substitutions and parsers are discovered. It's currently loading a global collection inside launch
upon entry point loading.
I was wondering if it wouldn't make sense to move away from that and make exposure somewhat more explicit, and maybe introducing namespaces in the process (which we do not have, we just fail upon tag name collision). Anyways, just a thought. No need to address this now, really.
Signed-off-by: ivanpauno <[email protected]>
Signed-off-by: ivanpauno <[email protected]>
Signed-off-by: ivanpauno <[email protected]>
Signed-off-by: ivanpauno <[email protected]>
Signed-off-by: ivanpauno <[email protected]>
Signed-off-by: ivanpauno <[email protected]>
Signed-off-by: ivanpauno <[email protected]>
…ink-install Signed-off-by: ivanpauno <[email protected]>
Signed-off-by: ivanpauno <[email protected]>
Signed-off-by: ivanpauno <[email protected]>
Checking if windows is happy after f15b0f2: (Edit) New failures on windows, checking locally. |
Signed-off-by: ivanpauno <[email protected]>
Signed-off-by: ivanpauno <[email protected]>
Signed-off-by: ivanpauno <[email protected]>
Signed-off-by: ivanpauno <[email protected]>
Signed-off-by: ivanpauno <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM but for a few minor comments. This is simply awesome, great job @ivanpauno !
keys = { | ||
int: 0, | ||
float: 1, | ||
bool: 2, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ivanpauno meta: I wonder if bool
should come before int
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, if not 0
will be converted to bool. If both are possible, it should be an int.
And maybe we should just delete that option. So the rules do not overlap.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If both are possible, it should be an int.
Ok, fair enough, Python coercion will do its job.
Signed-off-by: ivanpauno <[email protected]>
@hidmic I've addressed all the comments in both PRs. I will run an extra CI after an approval. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM!
…os2#226) Signed-off-by: ivanpauno <[email protected]>
See ros2/design#207 and ros2/design#208.
Targets all the tasks of #163 (comment) (except the last three).
This is already functional.
TODOs (in importance order):
Action and Substitution parsing methods should be moved to the classes (see
Add frontend parsing methods for Node, ExecutableInPackage and FindPackage substitution launch_ros#23) (nice to have). ✔️
TestEntity
to unit test the parsing methods (nice to have).That is a really tricky tasks (nice to have).
P.S.:
launch_frontend/launch_frontend/parse_substitution.py
(later modified by me) andlaunch_frontend/launch_frontend/grammar.lark
were contributed by @hidmic.Depends on #235 (after 3c1c1e9) (already merged).