Skip to content

Commit 7d63cc0

Browse files
committed
🐛 Fixes enum34 not found error when switching Conda environments
💥 BREAKING CHANGE: The ROS dist-packages path is now fully removed from the PYTHONPATH when inside an Conda environment. Before: ROS environment site-packages folder was placed before the ROS dist-packages path. After: Now the ROS dist-packages folder is removed from the PYTHONPATH when inside an anaconda environment. This was done as this method caused `module enum34 not found` error when changing between Conda Environments
1 parent 483ed11 commit 7d63cc0

File tree

1 file changed

+119
-114
lines changed

1 file changed

+119
-114
lines changed

ros_conda_wrapper_rc

Lines changed: 119 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ ROS_CONDA_WRAPPER_PROGNAME="ROS Conda wrapper"
1111
ROS_CONDA_WRAPPER_ERROR=false
1212
ROS_CONDA_WRAPPER_VERSION="1.0.1"
1313
ROS_CONDA_CONFIG_FILE_NAME="${HOME}/.ros_conda_wrapper_rc_cfg"
14+
ROS_CONDA_ROS_PYTHONPATH_BACKUP=()
1415

1516
# Bash echo colours
1617
ORANGE_CC='\033[0;33m'
@@ -123,30 +124,39 @@ function _ros_conda_path_fix_outside() {
123124
# This function makes sure the anaconda bin and condabin folders
124125
# are further on the path than the /usr/bin, /bin and
125126
# /opt/ros/<ROSVERSION>/bin when outside an anaconda environment.
126-
# It further makes sure that the anaconda site packages folder is
127-
# removed from the $PYTHONPATH.
127+
128+
# Create local variables
129+
local path_changed=false
130+
131+
# Split PATH into array and get path length
132+
local defaultIFS="$IFS"; IFS=$' \t\n'; local path_array=(${PATH//:/ }); IFS="$defaultIFS"
133+
local arr_len="${#path_array[@]}"
128134

129135
# Make sure the Conda paths are at the end of the PATH variable
130136
ros_conda_path_array=(
131137
"${ROS_CONDA_CONDA_PATH}/condabin"
132138
"${ROS_CONDA_CONDA_PATH}/bin"
133139
"${ROS_CONDA_CONDA_PATH}/envs/${CONDA_DEFAULT_ENV}/bin"
134140
)
135-
local path_changed=false
136141
for path in "${ros_conda_path_array[@]}"; do
137142
if [[ :${PATH}: == *:"${path}":* ]] ; then
138143

139-
# Remove all occurances from PATH variable
144+
# Remove all occurrences from PATH variable
140145
while [[ ":${PATH}:" == *:"${path}":* ]] ; do
146+
141147
# Remove path from PATH variable
142-
PATH=${PATH//":${path}:"/":"} # delete any instances in the middle
143-
PATH=${PATH/#"${path}:"/} # delete any instance at the beginning
144-
PATH=${PATH/%":${path}"/} # delete any instance in the at the end
148+
if [[ "${arr_len}" -eq 1 ]]; then # If Path length is 1
149+
PATH=${PATH//"${path}"/} # delete instance
150+
else
151+
PATH=${PATH//":${path}:"/":"} # delete any instances in the middle
152+
PATH=${PATH/#"${path}:"/} # delete any instance at the beginning
153+
PATH=${PATH/%":${path}"/} # delete any instance in the at the end
154+
fi
145155
done
146156

147157
# Add path to the end
148158
PATH="${PATH:+${PATH}:}${path}"
149-
local path_changed=true
159+
path_changed=true
150160
fi
151161
done
152162

@@ -161,28 +171,38 @@ function _ros_conda_path_fix_inside() {
161171
# are earlier on the path than the /usr/bin, /bin and
162172
# /opt/ros/<ROSVERSION>/bin when inside an anaconda environment.
163173

174+
# Create local variables
175+
local path_changed=false
176+
177+
# Split PATH into array and get path length
178+
local defaultIFS="$IFS"; IFS=$' \t\n'; local path_array=(${PATH//:/ }); IFS="$defaultIFS"
179+
local arr_len="${#path_array[@]}"
180+
164181
# Make sure the Conda paths are at the beginning of the PATH variable
165182
ros_conda_path_array=(
166183
"${ROS_CONDA_CONDA_PATH}/condabin"
167184
"${ROS_CONDA_CONDA_PATH}/bin"
168185
"${ROS_CONDA_CONDA_PATH}/envs/${CONDA_DEFAULT_ENV}/bin"
169186
)
170-
local path_changed=false
171187
for path in "${ros_conda_path_array[@]}"; do
172188
if [[ ":${PATH}:" == *:"${path}":* ]] ; then
173189

174-
# Remove all occurances from PATH variable
190+
# Remove all occurrences from PATH variable
175191
while [[ ":${PATH}:" == *:"${path}":* ]] ; do
176192

177193
# Remove path from PATH variable
178-
PATH=${PATH//":${path}:"/":"} # delete any instances in the middle
179-
PATH=${PATH/#"${path}:"/} # delete any instance at the beginning
180-
PATH=${PATH/%":${path}"/} # delete any instance in the at the end
194+
if [[ "${arr_len}" -eq 1 ]]; then # If Path length is 1
195+
PATH=${PATH//"${path}"/} # delete instance
196+
else
197+
PATH=${PATH//":${path}:"/":"} # delete any instances in the middle
198+
PATH=${PATH/#"${path}:"/} # delete any instance at the beginning
199+
PATH=${PATH/%":${path}"/} # delete any instance in the at the end
200+
fi
181201
done
182202

183-
# Prepent to PATH variable
203+
# Prepend to PATH variable
184204
PATH="${path}${PATH:+:${PATH}}"
185-
local path_changed=true
205+
path_changed=true
186206
fi
187207
done
188208

@@ -193,69 +213,66 @@ function _ros_conda_path_fix_inside() {
193213
}
194214

195215
function _ros_conda_pythonpath_fix_outside() {
196-
# This function makes sure the anaconda site packages folder
197-
# is removed from the PYTHONPATH when outside an anaconda
198-
# environment.
216+
# This function makes sure that the ROS python dist-packages path
217+
# IS present on the PYTHONPATH when inside a Conda environment.
199218

200-
# Remove any Conda site-packages folder from the PYTHONPATH
219+
# Create local variables
201220
local path_changed=false
202-
while [[ ":${PYTHONPATH}:" == *:"${ROS_CONDA_CONDA_PATH}"*"site-packages":* ]]; do
203221

204-
# Remove path from PATH variable
205-
PYTHONPATH=${PYTHONPATH//":${ROS_CONDA_CONDA_PATH}"*"site-packages:"/":"} # delete any instances in the middle
206-
PYTHONPATH=${PYTHONPATH/#"${ROS_CONDA_CONDA_PATH}"*"site-packages:"/} # delete any instance at the beginning
207-
PYTHONPATH=${PYTHONPATH/%":${ROS_CONDA_CONDA_PATH}"*"site-packages"/} # delete any instance in the at the end
208-
local path_changed=true
222+
# Restore ROS python paths if they are not yet found on the PYTHONPATH
223+
for ros_python_path in "${ROS_CONDA_ROS_PYTHONPATH_BACKUP[@]}"; do
224+
if [[ -d "$ros_python_path" ]]; then
225+
if [[ ":$PYTHONPATH:" != *":${ros_python_path}:"* ]]; then
226+
227+
# Prepend to PYTHONPATH variable
228+
PYTHONPATH="$ros_python_path${PYTHONPATH:+:${PYTHONPATH}}"
229+
path_changed=true
230+
fi
231+
fi
209232
done
210233

211-
# Export PATH
234+
# Export PYTHONPATH
212235
if [[ "$path_changed" = true ]]; then
236+
237+
# Export PYTHONPATH
213238
export PYTHONPATH
214239
fi
215240
}
216241

217242
function _ros_conda_pythonpath_fix_inside() {
218-
# This function makes sure that the anaconda site packages
219-
# folder comes before the ROS melodic dist-packages folder
220-
# when inside a anaconda environment.
243+
# This function makes sure that the ROS python dist-packages path
244+
# is NOT present on the PYTHONPATH when inside a Conda environment.
221245

222-
# Get Conda environment name and path
223-
if [[ "$CONDA_DEFAULT_ENV" == "base" || "$CONDA_DEFAULT_ENV" == "root" ]]; then
224-
local _conda_site_packages_path="$CONDA_BASE_SITE_PACKAGES_PATH"
225-
else
226-
local _conda_site_packages_path="$(find ${ROS_CONDA_CONDA_PATH}/envs/${CONDA_DEFAULT_ENV}/lib -iname site-packages 2> /dev/null)"
227-
fi
246+
# Create local variables
247+
local path_changed=false
248+
local new_pythonpath_array=()
228249

229-
# Remove any previously added site-packages folder from the PYTHONPATH
230-
while [[ ":${PYTHONPATH}:" == *:"${ROS_CONDA_CONDA_PATH}"*"site-packages":* ]]; do
250+
# Split PYTHONPATH or PYTHONPATH into array
251+
local defaultIFS="$IFS"; IFS=$' \t\n'; local pythonpath_array=(${PYTHONPATH//:/ }); IFS="$defaultIFS"
231252

232-
# Remove path from PATH variable
233-
PYTHONPATH=${PYTHONPATH//":${ROS_CONDA_CONDA_PATH}"*"site-packages:"/":"} # delete any instances in the middle
234-
PYTHONPATH=${PYTHONPATH/#"${ROS_CONDA_CONDA_PATH}"*"site-packages:"/} # delete any instance at the beginning
235-
PYTHONPATH=${PYTHONPATH/%":${ROS_CONDA_CONDA_PATH}"*"site-packages"/} # delete any instance in the at the end
236-
done
253+
# Loop through the paths and retrieve the python paths
254+
for path in "${pythonpath_array[@]}"; do
237255

238-
# Prepend Conda site-packages folder to the PYTHONPATH
239-
if [[ -d "$_conda_site_packages_path" ]]; then
240-
if [[ ":$PYTHONPATH:" != *":${_conda_site_packages_path}:"* ]]; then
256+
# Test if path is ros path
257+
if [[ "$path" == "/opt/ros/"*"/dist-packages" ]]; then
241258

242-
# Add Conda site-packages folder to PYTHONPATH
243-
PYTHONPATH="${_conda_site_packages_path}:${PYTHONPATH}"
259+
# Add to ROS_PYTHONPATH_BACKUP array
260+
ROS_CONDA_ROS_PYTHONPATH_BACKUP+=("${path}")
261+
path_changed=true
244262
else
245263

246-
# Remove any Conda site-packages folder from the PYTHONPATH if it exists
247-
while [[ ":$PYTHONPATH:" == *":${_conda_site_packages_path}:"* ]]; do
248-
# Remove path from PATH variable
249-
PYTHONPATH=${PYTHONPATH//":${_conda_site_packages_path}:"/":"} # delete any instances in the middle
250-
PYTHONPATH=${PYTHONPATH/#"${_conda_site_packages_path}:"/} # delete any instance at the beginning
251-
PYTHONPATH=${PYTHONPATH/%":${_conda_site_packages_path}"/} # delete any instance in the at the end
252-
done
253-
254-
# Add Conda site-packages folder to PYTHONPATH
255-
PYTHONPATH="${_conda_site_packages_path}:${PYTHONPATH}"
264+
# Pass to new PYTHONPATH array
265+
new_pythonpath_array+=("${path}")
256266
fi
267+
done
268+
269+
# Export PYTHONPATH
270+
if [[ "$path_changed" = true ]]; then
257271

258-
# Export PATH
272+
# Convert new PYTHONPATH back to : delimited string
273+
PYTHONPATH=$(IFS=$':'; echo "${new_pythonpath_array[*]}") # Converting bash array back into a delimited string
274+
275+
# Export PYTHONPATH
259276
export PYTHONPATH
260277
fi
261278
}
@@ -849,7 +866,7 @@ function _ros_conda_source_wrapper() {
849866
local had_error=false
850867

851868
# Execute source command
852-
\source "$@" 2>/dev/null || had_error=true
869+
\source "$@" || had_error=true
853870

854871
# Fix paths if source was successful
855872
if [[ "$had_error" == false ]]; then
@@ -871,40 +888,34 @@ function _ros_conda_source_wrapper() {
871888

872889
# Execute source command
873890
local had_error=false
874-
\source "$@" 2>/dev/null || had_error=true
891+
\source "$@" || had_error=true
875892

876893
# Fix paths if source was successful
877894
if [[ "$had_error" == false ]]; then
878895

879896
# Get full path
880897
local sourced_path="$1"
881-
case sourced_path in
882-
/*)
883-
local sourced_path_absolute="$sourced_path"
884-
;;
885-
./*)
886-
887-
local sourced_path_absolute="${PWD}/${sourced_path:2}"
888-
;;
889-
*)
890-
local sourced_path_absolute="${PWD}/${sourced_path}"
891-
;;
892-
esac
893-
894-
# Check if sourced file was inside catkin_ws
895-
if [[ -e "$(dirname $(dirname "${sourced_path_absolute}"))/.catkin_workspace" ]]; then
896-
897-
# Check if your inside or outside an anaconda environment
898-
if [[ ! -z "$CONDA_DEFAULT_ENV" ]]; then # Inside environment
899-
900-
# Fix PATH and PYTHONPATH
901-
_ros_conda_path_fix_inside
902-
_ros_conda_pythonpath_fix_inside
903-
else
898+
local sourced_path_absolute="$(cd $(dirname ${sourced_path}) 2>/dev/null && pwd -P)/$(basename ${sourced_path})"
899+
if [[ -e "$sourced_path_absolute" ]]; then
900+
901+
# Get main catkin_ws path
902+
local catkin_ws_path="$(dirname $(dirname ${sourced_path_absolute}))"
904903

905-
# Fix PATH and PYTHONPATH
906-
_ros_conda_path_fix_outside
907-
_ros_conda_pythonpath_fix_outside
904+
# Check if sourced file was inside catkin_ws
905+
if [[ -e "${catkin_ws_path}/.catkin_workspace" || -e "${catkin_ws_path}/.catkin_tools" ]]; then
906+
907+
# Check if your inside or outside an anaconda environment
908+
if [[ ! -z "$CONDA_DEFAULT_ENV" ]]; then # Inside environment
909+
910+
# Fix PATH and PYTHONPATH
911+
_ros_conda_path_fix_inside
912+
_ros_conda_pythonpath_fix_inside
913+
else
914+
915+
# Fix PATH and PYTHONPATH
916+
_ros_conda_path_fix_outside
917+
_ros_conda_pythonpath_fix_outside
918+
fi
908919
fi
909920
fi
910921
fi
@@ -938,7 +949,7 @@ function _ros_conda_dot_source_wrapper() {
938949
local had_error=false
939950

940951
# Execute source command
941-
\. "$@" 2>/dev/null || had_error=true
952+
\. "$@" || had_error=true
942953

943954
# Fix paths if source was successful
944955
if [[ "$had_error" == false ]]; then
@@ -960,40 +971,34 @@ function _ros_conda_dot_source_wrapper() {
960971

961972
# Execute source command
962973
local had_error=false
963-
\. "$@" 2>/dev/null || had_error=true
974+
\. "$@" || had_error=true
964975

965976
# Fix paths if source was successful
966977
if [[ "$had_error" == false ]]; then
967978

968979
# Get full path
969980
local sourced_path="$1"
970-
case sourced_path in
971-
/*)
972-
local sourced_path_absolute="$sourced_path"
973-
;;
974-
./*)
975-
976-
local sourced_path_absolute="${PWD}/${sourced_path:2}"
977-
;;
978-
*)
979-
local sourced_path_absolute="${PWD}/${sourced_path}"
980-
;;
981-
esac
982-
983-
# Check if sourced file was inside catkin_ws
984-
if [[ -e "$(dirname $(dirname "${sourced_path_absolute}"))/.catkin_workspace" ]]; then
985-
986-
# Check if your inside or outside an anaconda environment
987-
if [[ ! -z "$CONDA_DEFAULT_ENV" ]]; then # Inside environment
988-
989-
# Fix PATH and PYTHONPATH
990-
_ros_conda_path_fix_inside
991-
_ros_conda_pythonpath_fix_inside
992-
else
981+
local sourced_path_absolute="$(cd $(dirname ${sourced_path}) 2>/dev/null && pwd -P)/$(basename ${sourced_path})"
982+
if [[ -e "$sourced_path_absolute" ]]; then
983+
984+
# Get main catkin_ws path
985+
local catkin_ws_path="$(dirname $(dirname ${sourced_path_absolute}))"
986+
987+
# Check if sourced file was inside catkin_ws
988+
if [[ -e "${catkin_ws_path}/.catkin_workspace" || -e "${catkin_ws_path}/.catkin_tools" ]]; then
993989

994-
# Fix PATH and PYTHONPATH
995-
_ros_conda_path_fix_outside
996-
_ros_conda_pythonpath_fix_outside
990+
# Check if your inside or outside an anaconda environment
991+
if [[ ! -z "$CONDA_DEFAULT_ENV" ]]; then # Inside environment
992+
993+
# Fix PATH and PYTHONPATH
994+
_ros_conda_path_fix_inside
995+
_ros_conda_pythonpath_fix_inside
996+
else
997+
998+
# Fix PATH and PYTHONPATH
999+
_ros_conda_path_fix_outside
1000+
_ros_conda_pythonpath_fix_outside
1001+
fi
9971002
fi
9981003
fi
9991004
fi

0 commit comments

Comments
 (0)