diff --git a/launch/launch/logging/__init__.py b/launch/launch/logging/__init__.py index b43aa6a46..988de8049 100644 --- a/launch/launch/logging/__init__.py +++ b/launch/launch/logging/__init__.py @@ -37,6 +37,29 @@ ] +def _get_logging_directory(): + """ + Get logging directory path. + + Uses various environment variables to construct a logging directory path. + + Use $ROS_LOG_DIR if ROS_LOG_DIR is set and not empty. + Otherwise, use $ROS_HOME/log, using ~/.ros for ROS_HOME if not set or if empty. + + It also expands '~' to the current user's home directory, + and normalizes the path, converting the path separator if necessary. + + :return: the path to the logging directory + """ + log_dir = os.environ.get('ROS_LOG_DIR') + if not log_dir: + log_dir = os.environ.get('ROS_HOME') + if not log_dir: + log_dir = os.path.join('~', '.ros') + log_dir = os.path.join(log_dir, 'log') + return os.path.normpath(os.path.expanduser(log_dir)) + + def _make_unique_log_dir(*, base_path): """ Make a unique directory for logging. @@ -93,7 +116,7 @@ def log_dir(self): """Get the current log directory, generating it if necessary.""" if self._log_dir is None: self._log_dir = _make_unique_log_dir( - base_path=os.path.join(os.path.expanduser('~'), '.ros/log') + base_path=_get_logging_directory() ) return self._log_dir diff --git a/launch/test/launch/test_logging.py b/launch/test/launch/test_logging.py index fc7aa945b..751dc8bcc 100644 --- a/launch/test/launch/test_logging.py +++ b/launch/test/launch/test_logging.py @@ -16,7 +16,9 @@ import logging import os +import pathlib import re +from unittest import mock import launch.logging @@ -249,3 +251,73 @@ def emit(self, record): assert path in outputs assert len(outputs[path]) == 1 assert outputs[path][0].endswith('baz') + + +def fake_make_unique_log_dir(*, base_path): + # Passthrough; do not create the directory + return base_path + + +@mock.patch('launch.logging._make_unique_log_dir', mock.MagicMock(wraps=fake_make_unique_log_dir)) +def test_get_logging_directory(): + launch.logging.launch_config.reset() + os.environ.pop('ROS_LOG_DIR', None) + os.environ.pop('ROS_HOME', None) + home = pathlib.Path.home() + assert str(home) + + # Default case without ROS_LOG_DIR or ROS_HOME being set (but with HOME) + default_dir = str(home / '.ros/log') + # This ensures that the launch config will check the environment again + launch.logging.launch_config.log_dir = None + assert launch.logging.launch_config.log_dir == default_dir + + # Use $ROS_LOG_DIR if it is set + my_log_dir_raw = '/my/ros_log_dir' + my_log_dir = str(pathlib.Path(my_log_dir_raw)) + os.environ['ROS_LOG_DIR'] = my_log_dir + launch.logging.launch_config.log_dir = None + assert launch.logging.launch_config.log_dir == my_log_dir + # Make sure it converts path separators when necessary + os.environ['ROS_LOG_DIR'] = my_log_dir_raw + launch.logging.launch_config.log_dir = None + assert launch.logging.launch_config.log_dir == my_log_dir + # Setting ROS_HOME won't change anything since ROS_LOG_DIR is used first + os.environ['ROS_HOME'] = '/this/wont/be/used' + launch.logging.launch_config.log_dir = None + assert launch.logging.launch_config.log_dir == my_log_dir + os.environ.pop('ROS_HOME', None) + # Empty is considered unset + os.environ['ROS_LOG_DIR'] = '' + launch.logging.launch_config.log_dir = None + assert launch.logging.launch_config.log_dir == default_dir + # Make sure '~' is expanded to the home directory + os.environ['ROS_LOG_DIR'] = '~/logdir' + launch.logging.launch_config.log_dir = None + assert launch.logging.launch_config.log_dir == str(home / 'logdir') + + os.environ.pop('ROS_LOG_DIR', None) + + # Without ROS_LOG_DIR, use $ROS_HOME/log + fake_ros_home = home / '.fakeroshome' + fake_ros_home_log_dir = str(fake_ros_home / 'log') + os.environ['ROS_HOME'] = str(fake_ros_home) + launch.logging.launch_config.log_dir = None + assert launch.logging.launch_config.log_dir == fake_ros_home_log_dir + # Make sure it converts path separators when necessary + my_ros_home_raw = '/my/ros/home' + my_ros_home_log_dir = str(pathlib.Path(my_ros_home_raw) / 'log') + os.environ['ROS_HOME'] = my_ros_home_raw + launch.logging.launch_config.log_dir = None + assert launch.logging.launch_config.log_dir == my_ros_home_log_dir + # Empty is considered unset + os.environ['ROS_HOME'] = '' + launch.logging.launch_config.log_dir = None + assert launch.logging.launch_config.log_dir == default_dir + # Make sure '~' is expanded to the home directory + os.environ['ROS_HOME'] = '~/.fakeroshome' + launch.logging.launch_config.log_dir = None + assert launch.logging.launch_config.log_dir == fake_ros_home_log_dir + + os.environ.pop('ROS_HOME', None) + launch.logging.launch_config.reset()