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

Avoid the limitation of not being able to import rclpy C extensions at module level [alternative] #420

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions rclpy/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,15 @@ if(WIN32 AND CMAKE_BUILD_TYPE STREQUAL "Debug")
endif()

# enables using the Python extensions from the build space for testing
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/test_rclpy/__init__.py" "")
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}_impl/__init__.py" "")

ament_python_install_package(${PROJECT_NAME})

function(set_properties _targetname _build_type)
set_target_properties(${_targetname} PROPERTIES
PREFIX ""
LIBRARY_OUTPUT_DIRECTORY${_build_type} "${CMAKE_CURRENT_BINARY_DIR}/test_${PROJECT_NAME}"
RUNTIME_OUTPUT_DIRECTORY${_build_type} "${CMAKE_CURRENT_BINARY_DIR}/test_${PROJECT_NAME}"
LIBRARY_OUTPUT_DIRECTORY${_build_type} "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}_impl"
RUNTIME_OUTPUT_DIRECTORY${_build_type} "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}_impl"
OUTPUT_NAME "_${_targetname}${PythonExtra_EXTENSION_SUFFIX}"
SUFFIX "${PythonExtra_EXTENSION_EXTENSION}")
endfunction()
Expand Down Expand Up @@ -67,7 +67,7 @@ function(configure_python_c_extension_library _library_name)
)

install(TARGETS ${_library_name}
DESTINATION "${PYTHON_INSTALL_DIR}/${PROJECT_NAME}"
DESTINATION "${PYTHON_INSTALL_DIR}/${PROJECT_NAME}_impl"
Copy link
Member

Choose a reason for hiding this comment

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

This package doesn't own the namespace rclpy_impl. If another package with that name installs its files they will collide with this one.

)
endfunction()

Expand Down Expand Up @@ -97,6 +97,11 @@ install(TARGETS rclpy_common
RUNTIME DESTINATION bin
)

install(FILES
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}_impl/__init__.py"
DESTINATION "${PYTHON_INSTALL_DIR}/${PROJECT_NAME}_impl"
)

add_library(
rclpy
SHARED src/rclpy/_rclpy.c
Expand Down
3 changes: 1 addition & 2 deletions rclpy/rclpy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
from typing import TYPE_CHECKING

from rclpy.context import Context
from rclpy.impl.implementation_singleton import rclpy_implementation
from rclpy.parameter import Parameter
from rclpy.task import Future
from rclpy.utilities import get_default_context
Expand All @@ -68,8 +69,6 @@ def init(*, args: List[str] = None, context: Context = None) -> None:
(see :func:`.get_default_context`).
"""
context = get_default_context() if context is None else context
# imported locally to avoid loading extensions on module import
from rclpy.impl.implementation_singleton import rclpy_implementation
return rclpy_implementation.rclpy_init(args if args is not None else sys.argv, context.handle)


Expand Down
9 changes: 2 additions & 7 deletions rclpy/rclpy/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
import threading
from typing import Callable

from rclpy.impl.implementation_singleton import rclpy_implementation


class Context:
"""
Expand All @@ -26,7 +28,6 @@ class Context:
"""

def __init__(self):
from rclpy.impl.implementation_singleton import rclpy_implementation
self._handle = rclpy_implementation.rclpy_create_context()
self._lock = threading.Lock()
self._callbacks = []
Expand All @@ -38,8 +39,6 @@ def handle(self):

def ok(self):
"""Check if context hasn't been shut down."""
# imported locally to avoid loading extensions on module import
from rclpy.impl.implementation_singleton import rclpy_implementation
with self._lock:
return rclpy_implementation.rclpy_ok(self._handle)

Expand All @@ -51,16 +50,12 @@ def _call_on_shutdown_callbacks(self):

def shutdown(self):
"""Shutdown this context."""
# imported locally to avoid loading extensions on module import
from rclpy.impl.implementation_singleton import rclpy_implementation
with self._lock:
rclpy_implementation.rclpy_shutdown(self._handle)
self._call_on_shutdown_callbacks()

def try_shutdown(self):
"""Shutdown this context, if not already shutdown."""
# imported locally to avoid loading extensions on module import
from rclpy.impl.implementation_singleton import rclpy_implementation
with self._lock:
if rclpy_implementation.rclpy_ok(self._handle):
rclpy_implementation.rclpy_shutdown(self._handle)
Expand Down
2 changes: 1 addition & 1 deletion rclpy/rclpy/impl/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

def _import(name):
try:
return importlib.import_module(name, package='rclpy')
return importlib.import_module(name, package='rclpy_impl')
except ImportError as e:
if e.path is not None and os.path.isfile(e.path):
e.msg += \
Expand Down
5 changes: 1 addition & 4 deletions rclpy/rclpy/utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

from rclpy.constants import S_TO_NS
from rclpy.context import Context
from rclpy.impl.implementation_singleton import rclpy_implementation

g_default_context = None
g_context_lock = threading.Lock()
Expand All @@ -37,8 +38,6 @@ def get_default_context(*, shutting_down=False):


def remove_ros_args(args=None):
# imported locally to avoid loading extensions on module import
from rclpy.impl.implementation_singleton import rclpy_implementation
return rclpy_implementation.rclpy_remove_ros_args(
args if args is not None else sys.argv)

Expand All @@ -63,8 +62,6 @@ def try_shutdown(*, context=None):


def get_rmw_implementation_identifier():
# imported locally to avoid loading extensions on module import
from rclpy.impl.implementation_singleton import rclpy_implementation
return rclpy_implementation.rclpy_get_rmw_implementation_identifier()


Expand Down
29 changes: 0 additions & 29 deletions rclpy/test/__init__.py
Original file line number Diff line number Diff line change
@@ -1,29 +0,0 @@
# Copyright 2016-2017 Open Source Robotics Foundation, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import importlib
import sys

assert 'rclpy' not in sys.modules, 'rclpy should not have been imported before running tests'

# this will make the extensions load from the build folder
import rclpy.impl # noqa
import test_rclpy # noqa


def _custom_import(name):
return importlib.import_module(name, package='test_rclpy')


rclpy.impl._import = _custom_import