Catkin plugin: use rosdep for dependency resolution #163

Merged
merged 1 commit into from Dec 18, 2015

Conversation

Projects
None yet
3 participants
Member

kyrofa commented Dec 11, 2015

The Catkin plugin currently does its own dependency resolution via manually parsing each package.xml. It makes some naive assumptions while doing so, and a tool already exists in ROS for doing this: rosdep. This PR gets rid of the manual parsing in favor of rosdep.

snapcraft/plugins/catkin.py
- def pull(self):
- super().pull()
- self._setup_deb_packages()
+ source_subdir = getattr(self.options, 'source_subdir', None)
@sergiusens

sergiusens Dec 11, 2015

Collaborator

this should always be defined in the schema (as in it will only fail due to a programmatic error) so there is no need to getattr

@kyrofa

kyrofa Dec 15, 2015

Member

It doesn't seem to be defined here in __init__() (particularly when unit testing), so getattr seems necessary unless I should make sure all tests explicitly pass it in.

@sergiusens

sergiusens Dec 15, 2015

Collaborator

Right, the options class used in the tests needs to have the attrib. If testing from the plugin handler itself, this should be created with the defaults.

@kyrofa

kyrofa Dec 16, 2015

Member

Ah, alright done. And you're right, it works fine :) .

+ 'Did you forget to add it to catkin-packages? If '
+ 'not, add the Ubuntu package containing it to '
+ 'stage-packages until you can get it into the '
+ 'rosdep database.'.format(dependency))
@sergiusens

sergiusens Dec 11, 2015

Collaborator

nice error message 😄

Collaborator

sergiusens commented Dec 11, 2015

to be continued

snapcraft/plugins/catkin.py
- tree = lxml.etree.parse(f)
+ def _find_dependencies(self):
+ if self.package_deps_found:
+ return
@elopio

elopio Dec 14, 2015

Member

It would be good to explain this in a comment, because it's weird. What are the cases that makes us call find_dependencies twice?

@kyrofa

kyrofa Dec 14, 2015

Member

Great question: I'm not sure (that was already in there). However, I believe it's because it's called from both pull() and build() (since both steps need to know it), but it doesn't want to find deps twice in case it's being run via snapcraft instead of snapcraft <step>.

@kyrofa

kyrofa Dec 15, 2015

Member

I'm going to fix this in the next PR.

snapcraft/plugins/catkin.py
- def _deps_from_packagesxml(self, f, pkg):
- tree = lxml.etree.parse(f)
+ def _find_dependencies(self):
@elopio

elopio Dec 14, 2015

Member

I would find this clearer if it is a get function that returns the dependencies, instead of a method with no return value that has a side effect of changing the dependencies attribute. Would that be hard to change?

@kyrofa

kyrofa Dec 14, 2015

Member

I don't believe so, although not saving it as an attribute would mean I'd have to interrogate the sources for their dependencies in both pull() and build(). Depending on the size of the workspace that could be a bit heavy.

@kyrofa

kyrofa Dec 14, 2015

Member

I might be able to refactor the build step to not need these though... I'll get back to you.

@elopio

elopio Dec 14, 2015

Member

ok. And if you need the sideeffect for efficiency, maybe split it in two: one _get, and one _update that calls the _get. Or if it makes a bigger mess, just add a docstring to what you have explaining the side effect.

@kyrofa

kyrofa Dec 15, 2015

Member

I'm going to fix this in the next PR.

snapcraft/plugins/catkin.py
- self._deb_packages.append(
- 'ros-'+self.options.rosversion+'-'+dep.replace('_', '-'))
+ for dependency in dependencies:
+ if not dependency:
@elopio

elopio Dec 14, 2015

Member

Why would get_dependencies return an invalid dependency? It seems better to make sure that it returns a clean list of valid deps than to check them on the caller.

@kyrofa

kyrofa Dec 14, 2015

Member

Yeah you're right, this should be checked in get_dependencies(). And to answer your question, it shouldn't, but since I'm parsing stdout I'm trying to account for small changes in the output of rosdep (e.g. an extra line here and there).

@kyrofa

kyrofa Dec 15, 2015

Member

Fixed.

snapcraft/plugins/catkin.py
+ # not a dependency of the project, and we don't want to bloat the .snap
+ # more than necessary. So we'll unpack it somewhere else, and use it
+ # from there.
+ logger.info("Fetching rosdep...")
@elopio

elopio Dec 14, 2015

Member

single quotes. I'm sorry, it's an obsession of mine :)

@kyrofa

kyrofa Dec 14, 2015

Member

Ah, no thanks! I've been trying to adhere but I knew I was going to miss some, haha :) .

snapcraft/plugins/catkin.py
+ ubuntu.get(['python-rosdep'])
+ ubuntu.unpack(self._rosdep_install_path)
+
+ logger.info("Initializing rosdep database...")
@elopio

elopio Dec 14, 2015

Member

single quotes.

snapcraft/plugins/catkin.py
+ raise RuntimeError(
+ 'Error initializing rosdep database: {}'.format(e.output))
+
+ logger.info("Updating rosdep database...")
@elopio

elopio Dec 14, 2015

Member

single quotes.

Member

kyrofa commented Dec 15, 2015

Unless there are more comments, I've either fixed the concerns here or will address them in a PR I'll make right after this one is merged.

snapcraft/tests/test_plugin_catkin.py
+ rosdep._rosdep_install_path, 'usr', 'lib', 'python2.7',
+ 'dist-packages') and
+ env['ROSDEP_SOURCE_PATH'] == rosdep._rosdep_sources_path
+ and env['ROS_HOME'] == rosdep._rosdep_cache_path and
@elopio

elopio Dec 17, 2015

Member

the and operator should be at the end of the line. flake8 complaints in xenial, it seems to ignore it in the travis's version.

@kyrofa

kyrofa Dec 17, 2015

Member

Ah interesting, okay. Yeah, it passes on trusty.

@kyrofa

kyrofa Dec 17, 2015

Member

Fixed.

snapcraft/tests/test_plugin_catkin.py
- self.assertFalse(self.ubuntu_mock.called,
- "Ubuntu packages were unexpectedly pulled down")
+ # Verify that rosdep was setup
+ self.rosdep_mock.assert_has_calls([
@elopio

elopio Dec 17, 2015

Member

This could be a checker method, you are using it twice.

@kyrofa

kyrofa Dec 17, 2015

Member

Fixed.

+ plugin.pull()
+ except FileNotFoundError:
+ self.fail('Unexpectedly raised an exception when the Catkin '
+ 'workspace was valid')
@elopio

elopio Dec 17, 2015

Member

I generally just leave the exception make the test fail, with a comment above the step that can raise the exception. The exception should say something useful already to understand what went wrong, otherwise the exception is not very good.
If you prefer the try catch, that's good too. Just not common in the projects I've worked.

@kyrofa

kyrofa Dec 17, 2015

Member

Normally I agree with you, but I'm explicitly making sure this code doesn't die if I know it can't find the file. I can make the failure message much more friendly than trying to navigate a traceback. There's also something to be said for making the tests fail if what I'm testing for doesn't pass rather than error. If an exception was raised as a result of some setup code then I feel like that's a valid error, but if it's exactly what I'm testing for I prefer to catch it and turn it into a failure.

snapcraft/tests/test_plugin_catkin.py
+ self.assertEqual(raised.exception.args[0],
@elopio

elopio Dec 17, 2015

Member

I think it's better to check
str(raised.exception)
instead of
raises.exception.args[0]

@kyrofa

kyrofa Dec 17, 2015

Member

face-palm so much better! Thank you :)

@sergiusens

sergiusens Dec 17, 2015

Collaborator

Do you prefer str(raised.exception) over raised.exception.__str__() ?
Just asking since other parts of the code base use the latter in tests.

@kyrofa

kyrofa Dec 17, 2015

Member

Fixed all of these.

@kyrofa

kyrofa Dec 17, 2015

Member

Ah, hey @sergiusens I missed your comment here. I feel like the former is better-looking, but I'm happy doing either.

snapcraft/tests/test_plugin_catkin.py
+ plugin.pull()
+
+ self.assertEqual(
+ raised.exception.args[0],
@elopio

elopio Dec 17, 2015

Member

same str(raised.exception)

snapcraft/tests/test_plugin_catkin.py
+ self.addCleanup(patcher.stop)
+
+ patcher = mock.patch('subprocess.check_output')
+ self.run_mock = patcher.start()
@elopio

elopio Dec 17, 2015

Member

I would understand the tests easier if the mock is called check_output_mock.

@kyrofa

kyrofa Dec 17, 2015

Member

Agreed, thank you! Copy-paste error, I think.

Member

elopio commented Dec 17, 2015

@kyrofa sorry it took so long.
As I told you, I'm 👍 here. I just left some PITA comments about the tests, that you can safely ignore.

Catkin plugin: use rosdep for dependency resolution.
The Catkin plugin currently does its own dependency resolution via
manually parsing each package.xml. It makes some naive assumptions
while doing so, and a tool already exists in ROS for doing this:
rosdep. This commit gets rid of the manual parsing in favor of
rosdep.

Signed-off-by: Kyle Fazzari <kyle@canonical.com>
Member

kyrofa commented Dec 17, 2015

@elopio excellent review, thank you! PITA comments are appreciated ;) . We'll just wait for @sergiusens to give his OK here, then.

@kyrofa kyrofa added the enhancement label Dec 17, 2015

self.package_deps_found = False
self.package_local_deps = {}
self._deb_packages = []
+ self.dependencies = []
@sergiusens

sergiusens Dec 18, 2015

Collaborator

is order important or can this be a set?

@kyrofa

kyrofa Dec 18, 2015

Member

It could indeed be a set, but it's completely removed in the next PR.

Collaborator

sergiusens commented Dec 18, 2015

Just one comment related to names; it is mostly a nitpick and from being away from the problem, but I'd like to see member names to be self.catkin_packages instead of self.packages and similar for the system counterparts; this also applies to the names the methods have.

Preemptive 👍 take the comment or leave it 😉

Member

kyrofa commented Dec 18, 2015

@sergiusens yeah, good point. There's a bit of technical debt to be paid there, but I'll take a look at that in the refactor coming up.

kyrofa added a commit that referenced this pull request Dec 18, 2015

Merge pull request #163 from kyrofa/catkin_use_rosdep
Catkin plugin: use rosdep for dependency resolution

@kyrofa kyrofa merged commit 20b33e4 into snapcore:master Dec 18, 2015

2 checks passed

continuous-integration/travis-ci/pr The Travis CI build passed
Details
coverage/coveralls Coverage increased (+0.02%) to 95.788%
Details

@kyrofa kyrofa deleted the kyrofa:catkin_use_rosdep branch Dec 18, 2015

kalikiana pushed a commit to kalikiana/snapcraft that referenced this pull request Apr 6, 2017

Merge pull request #163 from kyrofa/catkin_use_rosdep
Catkin plugin: use rosdep for dependency resolution
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment