ros2 / ros2cli

ROS 2 command line interface tools
Apache License 2.0
173 stars 159 forks source link

`ros2 pkg create` with `ament_python` and `--library-name` returns `ModuleNotFoundError:` when library is used in another package. #833

Open mmmarinho opened 1 year ago

mmmarinho commented 1 year ago

Bug report

When creating an ament_python package with the --library-name option, the setup.py might be missing a few directives for the library created this way. I might have misused this, so I apologize in advance if that's the case.

Required Info:

Steps to reproduce issue

The following bash script should fully reproduce the situation in a clean environment, namely:

  1. Create an ament_python package with a sample_python_library
  2. Add a function to the library
  3. Create another ament_python package with a node that uses the library
  4. Source and run
#  Source ROS 2 Humble
source /opt/ros/humble/setup.bash

# Create workspace
mkdir -p tmp/ros2_ws/src
cd tmp/ros2_ws/src

# Create library package
ros2 pkg create python_package_with_a_library \
--build-type ament_python \
--library-name sample_python_library

# Add example function to library
echo "
def example_function():
    print('Hello')

" > python_package_with_a_library/python_package_with_a_library/sample_python_library/__init__.py

# Create a package that uses the library, with the correct dependencies
ros2 pkg create python_package_that_uses_the_library \
--dependencies python_package_with_a_library \
--build-type ament_python \
--node-name node_that_uses_the_library

# Replace the contents of the Node such that it uses the library's example function
echo "
from python_package_with_a_library.sample_python_library import example_function

def main():
    example_function()

if __name__ == '__main__':
    main()

" > python_package_that_uses_the_library/python_package_that_uses_the_library/node_that_uses_the_library.py

# Build and source
cd ..
colcon build
source install/setup.bash

# Run
ros2 run python_package_that_uses_the_library node_that_uses_the_library 

Expected behavior

I expected that the library would be correctly configured and the output would be

Hello

Actual behavior

A ModuleNotFoundError.

Traceback (most recent call last):
  File "/home/murilo/Desktop/tmp/ros2_ws/install/python_package_that_uses_the_library/lib/python_package_that_uses_the_library/node_that_uses_the_library", line 33, in <module>
    sys.exit(load_entry_point('python-package-that-uses-the-library==0.0.0', 'console_scripts', 'node_that_uses_the_library')())
  File "/home/murilo/Desktop/tmp/ros2_ws/install/python_package_that_uses_the_library/lib/python_package_that_uses_the_library/node_that_uses_the_library", line 25, in importlib_load_entry_point
    return next(matches).load()
  File "/usr/lib/python3.10/importlib/metadata/__init__.py", line 171, in load
    module = import_module(match.group('module'))
  File "/usr/lib/python3.10/importlib/__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1050, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1027, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1006, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 688, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 883, in exec_module
  File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
  File "/home/murilo/Desktop/tmp/ros2_ws/install/python_package_that_uses_the_library/lib/python3.10/site-packages/python_package_that_uses_the_library/node_that_uses_the_library.py", line 2, in <module>
    from python_package_with_a_library.sample_python_library import example_function
ModuleNotFoundError: No module named 'python_package_with_a_library.sample_python_library'
[ros2run]: Process exited with failure 1

Additional information

Indeed, inspecting tmp/ros2_ws/install/python_package_with_a_library/lib/python3.10/site-packages/python_package_with_a_library reveals that the library is not there:

__init__.py
__pycache__

Modifying the setup.py as shown below seems to fix this issue. However, given that the library package was created from the template provided by ros2 pkg create with the --library-name option, I expected that setup.py might have all the necessary directives.

from setuptools import setup, find_packages

package_name = 'python_package_with_a_library'

setup(
    name=package_name,
    version='0.0.0',
    packages=find_packages(exclude=['test']),
    data_files=[
        ('share/ament_index/resource_index/packages',
            ['resource/' + package_name]),
        ('share/' + package_name, ['package.xml']),
    ],
    install_requires=['setuptools'],
    zip_safe=True,
    maintainer='murilo',
    maintainer_email='murilomarinho@ieee.org',
    description='TODO: Package description',
    license='TODO: License declaration',
    tests_require=['pytest'],
    entry_points={
        'console_scripts': [
        ],
    },
)

After this modification, tmp/ros2_ws/install/python_package_with_a_library/lib/python3.10/site-packages/python_package_with_a_library has the library as expected

__init__.py  
__pycache__
sample_python_library

Edit: fixed a few typos

iuhilnehc-ynos commented 1 year ago

I can reproduce this issue with humble but not with iron/rolling. It seems https://github.com/ros2/ros2cli/pull/801 should be backported into humble.

clalancette commented 1 year ago

Good call, I've now asked the bot to do that.