ros_loader.py
Go to the documentation of this file.
00001 import time
00002 #!/usr/bin/env python
00003 # Software License Agreement (BSD License)
00004 #
00005 # Copyright (c) 2012, Willow Garage, Inc.
00006 # All rights reserved.
00007 #
00008 # Redistribution and use in source and binary forms, with or without
00009 # modification, are permitted provided that the following conditions
00010 # are met:
00011 #
00012 #  * Redistributions of source code must retain the above copyright
00013 #    notice, this list of conditions and the following disclaimer.
00014 #  * Redistributions in binary form must reproduce the above
00015 #    copyright notice, this list of conditions and the following
00016 #    disclaimer in the documentation and/or other materials provided
00017 #    with the distribution.
00018 #  * Neither the name of Willow Garage, Inc. nor the names of its
00019 #    contributors may be used to endorse or promote products derived
00020 #    from this software without specific prior written permission.
00021 #
00022 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00023 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00024 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00025 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00026 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00027 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00028 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00029 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00030 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00031 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00032 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00033 # POSSIBILITY OF SUCH DAMAGE.
00034 
00035 import roslib
00036 import rospy
00037 
00038 from threading import Lock
00039 
00040 """ ros_loader contains methods for dynamically loading ROS message classes at
00041 runtime.  It's achieved by using roslib to load the manifest files for the
00042 package that the respective class is contained in.
00043 
00044 Methods typically return the requested class or instance, or None if not found
00045 """
00046 
00047 # Variable containing the loaded classes
00048 _loaded_msgs = {}
00049 _loaded_srvs = {}
00050 _msgs_lock = Lock()
00051 _srvs_lock = Lock()
00052 _manifest_lock = Lock()
00053 
00054 
00055 class InvalidTypeStringException(Exception):
00056     def __init__(self, typestring):
00057         Exception.__init__(self, "%s is not a valid type string" % typestring)
00058 
00059 
00060 class InvalidPackageException(Exception):
00061     def __init__(self, package, original_exception):
00062         Exception.__init__(self,
00063            "Unable to load the manifest for package %s. Caused by: %s"
00064            % (package, str(original_exception))
00065        )
00066 
00067 
00068 class InvalidModuleException(Exception):
00069     def __init__(self, modname, subname, original_exception):
00070         Exception.__init__(self,
00071            "Unable to import %s.%s from package %s. Caused by: %s"
00072            % (modname, subname, modname, str(original_exception))
00073         )
00074 
00075 
00076 class InvalidClassException(Exception):
00077     def __init__(self, modname, subname, classname, original_exception):
00078         Exception.__init__(self,
00079            "Unable to import %s class %s from package %s. Caused by %s"
00080            % (subname, classname, modname, str(original_exception))
00081         )
00082 
00083 
00084 def get_message_class(typestring):
00085     """ Loads the message type specified.
00086 
00087     Returns the loaded class, or throws exceptions on failure """
00088     return _get_msg_class(typestring)
00089 
00090 
00091 def get_service_class(typestring):
00092     """ Loads the service type specified.
00093 
00094     Returns the loaded class, or None on failure """
00095     return _get_srv_class(typestring)
00096 
00097 
00098 def get_message_instance(typestring):
00099     """ If not loaded, loads the specified type.
00100     Then returns an instance of it, or None. """
00101     cls = get_message_class(typestring)
00102     return cls()
00103 
00104 
00105 def get_service_instance(typestring):
00106     """ If not loaded, loads the specified type.
00107     Then returns an instance of it, or None. """
00108     cls = get_service_class(typestring)
00109     return cls()
00110 
00111 
00112 def get_service_request_instance(typestring):
00113     cls = get_service_class(typestring)
00114     return cls._request_class()
00115 
00116 
00117 def get_service_response_instance(typestring):
00118     cls = get_service_class(typestring)
00119     return cls._response_class()
00120 
00121 
00122 def _get_msg_class(typestring):
00123     """ If not loaded, loads the specified msg class then returns an instance
00124     of it
00125 
00126     Throws various exceptions if loading the msg class fails """
00127     global _loaded_msgs, _msgs_lock
00128     return _get_class(typestring, "msg", _loaded_msgs, _msgs_lock)
00129 
00130 
00131 def _get_srv_class(typestring):
00132     """ If not loaded, loads the specified srv class then returns an instance
00133     of it
00134 
00135     Throws various exceptions if loading the srv class fails """
00136     global _loaded_srvs, _srvs_lock
00137     return _get_class(typestring, "srv", _loaded_srvs, _srvs_lock)
00138 
00139 
00140 def _get_class(typestring, subname, cache, lock):
00141     """ If not loaded, loads the specified class then returns an instance
00142     of it.
00143 
00144     Loaded classes are cached in the provided cache dict
00145 
00146     Throws various exceptions if loading the msg class fails """
00147 
00148     # First, see if we have this type string cached
00149     cls = _get_from_cache(cache, lock, typestring)
00150     if cls is not None:
00151         return cls
00152 
00153     # Now normalise the typestring
00154     modname, classname = _splittype(typestring)
00155     norm_typestring = modname + "/" + classname
00156 
00157     # Check to see if the normalised type string is cached
00158     cls = _get_from_cache(cache, lock, norm_typestring)
00159     if cls is not None:
00160         return cls
00161 
00162     # Load the class
00163     cls = _load_class(modname, subname, classname)
00164 
00165     # Cache the class for both the regular and normalised typestring
00166     _add_to_cache(cache, lock, typestring, cls)
00167     _add_to_cache(cache, lock, norm_typestring, cls)
00168 
00169     return cls
00170 
00171 
00172 def _load_class(modname, subname, classname):
00173     """ Loads the manifest and imports the module that contains the specified
00174     type.
00175 
00176     Logic is similar to that of roslib.message.get_message_class, but we want
00177     more expressive exceptions.
00178 
00179     Returns the loaded module, or None on failure """
00180     global loaded_modules
00181 
00182     try:
00183         with _manifest_lock:
00184             # roslib maintains a cache of loaded manifests, so no need to duplicate
00185             roslib.launcher.load_manifest(modname)
00186     except Exception as exc:
00187         raise InvalidPackageException(modname, exc)
00188 
00189     try:
00190         pypkg = __import__('%s.%s' % (modname, subname))
00191     except Exception as exc:
00192         raise InvalidModuleException(modname, subname, exc)
00193 
00194     try:
00195         return getattr(getattr(pypkg, subname), classname)
00196     except Exception as exc:
00197         raise InvalidClassException(modname, subname, classname, exc)
00198 
00199 
00200 def _splittype(typestring):
00201     """ Split the string the / delimiter and strip out empty strings
00202 
00203     Performs similar logic to roslib.names.package_resource_name but is a bit
00204     more forgiving about excess slashes
00205     """
00206     splits = [x for x in typestring.split("/") if x]
00207     if len(splits) == 2:
00208         return splits
00209     raise InvalidTypeStringException(typestring)
00210 
00211 
00212 def _add_to_cache(cache, lock, key, value):
00213     lock.acquire()
00214     cache[key] = value
00215     lock.release()
00216 
00217 
00218 def _get_from_cache(cache, lock, key):
00219     """ Returns the value for the specified key from the cache.
00220     Locks the lock before doing anything. Returns None if key not in cache """
00221     lock.acquire()
00222     ret = None
00223     if key in cache:
00224         ret = cache[key]
00225     lock.release()
00226     return ret


rosbridge_library
Author(s): Jonathan Mace
autogenerated on Thu Jun 6 2019 21:51:43