Add frontend module in launch, launch_xml and launch_yaml packages#226
Add frontend module in launch, launch_xml and launch_yaml packages#226
Conversation
hidmic
left a comment
There was a problem hiding this comment.
@ivanpauno alright, looking good, thanks for pushing this!
c46b260 to
0d2e6ac
Compare
hidmic
left a comment
There was a problem hiding this comment.
Second pass, sorry for the delay to review :(
992f531 to
b28736f
Compare
hidmic
left a comment
There was a problem hiding this comment.
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.
@ivanpauno meta: IMHO, type deduction documentation is design document material.
There was a problem hiding this comment.
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.
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.
@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.
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.
Escaping quotes inside of quotes is ok by me.
5e1a0b4 to
9c3a9f6
Compare
|
Rebased with master again. |
hidmic
left a comment
There was a problem hiding this comment.
@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.
@ivanpauno why not replace 'guess' by the absence of a type and make it the default?
There was a problem hiding this comment.
Because the type that is specified most of the times is str (substitutions are str).
| 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.
@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.
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.
@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.
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.
Right, my point being that these should be instance methods.
There was a problem hiding this comment.
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.
@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.
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.
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.
I still wonder if it wouldn't be better to use typing objects instead of plain strings.
There was a problem hiding this comment.
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.
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.
@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.
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.
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 <ivanpauno@ekumenlabs.com>
Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>
Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>
Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>
Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>
Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>
Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>
Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>
Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>
Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>
Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>
Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>
Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>
…ink-install Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>
Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>
Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>
|
Checking if windows is happy after f15b0f2: (Edit) New failures on windows, checking locally. |
Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>
Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>
Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>
Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>
Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>
There was a problem hiding this comment.
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.
@ivanpauno meta: I wonder if bool should come before int
There was a problem hiding this comment.
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.
If both are possible, it should be an int.
Ok, fair enough, Python coercion will do its job.
Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>
|
@hidmic I've addressed all the comments in both PRs. I will run an extra CI after an approval. |
…os2#226) Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>
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). ✔️
TestEntityto 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.larkwere contributed by @hidmic.Depends on #235 (after 3c1c1e9) (already merged).