ModuleManager.py
Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 # -*- coding: euc-jp -*-
00003 
00004 ##
00005 # @file ModuleManager.py
00006 # @brief Loadable modules manager class
00007 # @date $Date: 2007/08/24$
00008 # @author Noriaki Ando <n-ando@aist.go.jp> and Shinji Kurihara
00009 #
00010 # Copyright (C) 2006-2008
00011 #     Task-intelligence Research Group,
00012 #     Intelligent Systems Research Institute,
00013 #     National Institute of
00014 #         Advanced Industrial Science and Technology (AIST), Japan
00015 #     All rights reserved.
00016 
00017 
00018 import string
00019 import sys,os
00020 import glob
00021 
00022 import OpenRTM_aist
00023 
00024 
00025 CONFIG_EXT    = "manager.modules.config_ext"
00026 CONFIG_PATH   = "manager.modules.config_path"
00027 DETECT_MOD    = "manager.modules.detect_loadable"
00028 MOD_LOADPTH   = "manager.modules.load_path"
00029 INITFUNC_SFX  = "manager.modules.init_func_suffix"
00030 INITFUNC_PFX  = "manager.modules.init_func_prefix"
00031 ALLOW_ABSPATH = "manager.modules.abs_path_allowed"
00032 ALLOW_URL     = "manager.modules.download_allowed"
00033 MOD_DWNDIR    = "manager.modules.download_dir"
00034 MOD_DELMOD    = "manager.modules.download_cleanup"
00035 MOD_PRELOAD   = "manager.modules.preload"
00036 
00037 
00038 
00039 ##
00040 # @if jp
00041 #
00042 # @brief モジュールマネージャクラス
00043 # @class ModuleManager
00044 #
00045 # モジュールのロード、アンロードなどを管理するクラス
00046 #
00047 # @since 0.4.0
00048 #
00049 # @else
00050 #
00051 # @biref ModuleManager class
00052 #
00053 # @endif
00054 class ModuleManager:
00055   """
00056   """
00057 
00058 
00059 
00060   ##
00061   # @if jp
00062   #
00063   # @brief コンストラクタ
00064   #
00065   # コンストラクタ。
00066   # 設定された Property オブジェクト内の情報を基に初期化を実行する。
00067   #
00068   # @param self
00069   # @param prop 初期化用プロパティ
00070   #
00071   # @else
00072   #
00073   # @brief constructor
00074   #
00075   # @endif
00076   def __init__(self, prop):
00077     self._properties = prop
00078 
00079     self._configPath = prop.getProperty(CONFIG_PATH).split(",")
00080     for i in range(len(self._configPath)):
00081       tmp = [self._configPath[i]]
00082       OpenRTM_aist.eraseHeadBlank(tmp)
00083       self._configPath[i] = tmp[0]
00084     self._loadPath = prop.getProperty(MOD_LOADPTH,"./").split(",")
00085     for i in range(len(self._loadPath)):
00086       tmp = [self._loadPath[i]]
00087       OpenRTM_aist.eraseHeadBlank(tmp)
00088       self._loadPath[i] = tmp[0]
00089 
00090     self._absoluteAllowed = OpenRTM_aist.toBool(prop.getProperty(ALLOW_ABSPATH),
00091                                                 "yes", "no", False)
00092 
00093     self._downloadAllowed = OpenRTM_aist.toBool(prop.getProperty(ALLOW_URL),
00094                                                 "yes", "no", False)
00095 
00096     self._initFuncSuffix = prop.getProperty(INITFUNC_SFX)
00097     self._initFuncPrefix = prop.getProperty(INITFUNC_PFX)
00098     self._modules = OpenRTM_aist.ObjectManager(self.DLLPred)
00099     self._rtcout = None
00100     self._mgr = OpenRTM_aist.Manager.instance()
00101 
00102   ##
00103   # @if jp
00104   #
00105   # @brief デストラクタ(未実装)
00106   #
00107   # @param self
00108   #
00109   # @else
00110   #
00111   # @brief destructor
00112   #
00113   # @endif
00114   def __del__(self):
00115     self.unloadAll()
00116 
00117 
00118   ##
00119   # @if jp
00120   # @class Error
00121   # @brief ファイル・オープン失敗例外処理用内部クラス
00122   # @else
00123   #
00124   # @endif
00125   class Error:
00126     def __init__(self, reason_):
00127       self.reason = reason_
00128 
00129 
00130 
00131   ##
00132   # @if jp
00133   # @class NotFound
00134   # @brief 未実装部,指定モジュール不明例外処理用内部クラス
00135   # @else
00136   #
00137   # @endif
00138   class NotFound:
00139     def __init__(self, name_):
00140       self.name = name_
00141 
00142 
00143 
00144   ##
00145   # @if jp
00146   # @class FileNotFound
00147   # @brief 指定ファイル不明例外処理用内部クラス
00148   # @else
00149   #
00150   # @endif
00151   class FileNotFound(NotFound):
00152     def __init__(self, name_):
00153       ModuleManager.NotFound.__init__(self, name_)
00154 
00155 
00156 
00157   ##
00158   # @if jp
00159   # @class ModuleNotFound
00160   # @brief 指定モジュール不明例外処理用内部クラス
00161   # @else
00162   #
00163   # @endif
00164   class ModuleNotFound(NotFound):
00165     def __init__(self, name_):
00166       ModuleManager.NotFound.__init__(self, name_)
00167 
00168 
00169 
00170   ##
00171   # @if jp
00172   # @class SymbolNotFound
00173   # @brief 指定シンボル不明例外処理用内部クラス
00174   # @else
00175   #
00176   # @endif
00177   class SymbolNotFound(NotFound):
00178     def __init__(self, name_):
00179       ModuleManager.NotFound.__init__(self, name_)
00180 
00181 
00182 
00183   ##
00184   # @if jp
00185   # @class NotAllowedOperation
00186   # @brief 指定操作禁止時例外処理用内部クラス
00187   # @else
00188   #
00189   # @endif
00190   class NotAllowedOperation(Error):
00191     def __init__(self, reason_):
00192       ModuleManager.Error.__init__(self, reason_)
00193       ModuleManager.Error.__init__(self, reason_)
00194 
00195 
00196 
00197   ##
00198   # @if jp
00199   # @class InvalidArguments
00200   # @brief 指定引数不正時例外処理用内部クラス
00201   # @else
00202   #
00203   # @endif
00204   class InvalidArguments(Error):
00205     def __init__(self, reason_):
00206       ModuleManager.Error.__init__(self, reason_)
00207 
00208 
00209 
00210   ##
00211   # @if jp
00212   # @class InvalidOperation
00213   # @brief 指定操作不正時例外処理用内部クラス
00214   # @else
00215   #
00216   # @endif
00217   class InvalidOperation(Error):
00218     def __init__(self, reason_):
00219       ModuleManager.Error.__init__(self, reason_)
00220 
00221 
00222 
00223   ##
00224   # @if jp
00225   #
00226   # @brief モジュールのロード、初期化
00227   #
00228   # 指定したファイルを共有ライブラリとしてロードするとともに、
00229   # 指定した初期化用オペレーションを実行する。
00230   # 
00231   # @param self
00232   # @param file_name ロード対象モジュール名 (.pyを除いたファイル名)
00233   # @param init_func 初期化処理用オペレーション(デフォルト値:None)
00234   #
00235   # @return 指定したロード対象モジュール名
00236   #
00237   # @else
00238   #
00239   # @brief Load module
00240   #
00241   #
00242   # @endif
00243   # std::string ModuleManager::load(const std::string& file_name,
00244   #                                 const std::string& init_func)
00245   def load(self, file_name, init_func=None):
00246     if not self._rtcout:
00247       self._rtcout = self._mgr.getLogbuf("ModuleManager")
00248 
00249     self._rtcout.RTC_TRACE("load(fname = %s)", file_name)
00250     if file_name == "":
00251       raise ModuleManager.InvalidArguments, "Invalid file name."
00252 
00253     if OpenRTM_aist.isURL(file_name):
00254       if not self._downloadAllowed:
00255         raise ModuleManager.NotAllowedOperation, "Downloading module is not allowed."
00256       else:
00257         raise ModuleManager.NotFound, "Not implemented."
00258 
00259     import_name = os.path.split(file_name)[-1]
00260     pathChanged=False
00261     file_path = None
00262     if OpenRTM_aist.isAbsolutePath(file_name):
00263       if not self._absoluteAllowed:
00264         raise ModuleManager.NotAllowedOperation, "Absolute path is not allowed"
00265       else:
00266         splitted_name = os.path.split(file_name)
00267         save_path = sys.path[:]
00268         sys.path.append(splitted_name[0])
00269         pathChanged = True
00270         import_name = splitted_name[-1]
00271         file_path = file_name
00272 
00273     else:
00274       file_path = self.findFile(file_name, self._loadPath)
00275       if not file_path:
00276         raise ModuleManager.InvalidArguments, "Invalid file name."
00277 
00278     if not self.fileExist(file_path):
00279       raise ModuleManager.FileNotFound, file_name
00280 
00281     if not pathChanged:
00282       splitted_name = os.path.split(file_path)
00283       sys.path.append(splitted_name[0])
00284 
00285     ext_pos = import_name.find(".py")
00286     if ext_pos > 0:
00287       import_name = import_name[:ext_pos]
00288     mo = __import__(str(import_name))
00289 
00290     if pathChanged:
00291       sys.path = save_path
00292 
00293     dll = self.DLLEntity(mo,OpenRTM_aist.Properties())
00294     dll.properties.setProperty("file_path",file_path)
00295     self._modules.registerObject(dll)
00296 
00297 
00298     if init_func is None:
00299       return file_name
00300 
00301     self.symbol(file_path,init_func)(self._mgr)
00302 
00303     return file_name
00304 
00305 
00306   ##
00307   # @if jp
00308   # @brief モジュールのアンロード
00309   #
00310   # 指定したロード済みモジュールをクローズし、アンロードする。
00311   #
00312   # @param self
00313   # @param file_name アンロード対象モジュール名
00314   #
00315   # @else
00316   # @brief Unload module
00317   # @endif
00318   def unload(self, file_name):
00319     dll = self._modules.find(file_name)
00320     if not dll:
00321       raise ModuleManager.NotFound, file_name
00322 
00323     self._modules.unregisterObject(file_name)
00324     return
00325 
00326 
00327   ##
00328   # @if jp
00329   # @brief 全モジュールのアンロード
00330   #
00331   # 全てのロード済みモジュールをアンロードする。
00332   #
00333   # @param self
00334   #
00335   # @else
00336   # @brief Unload all modules
00337   # @endif
00338   def unloadAll(self):
00339     dlls = self._modules.getObjects()
00340     
00341     for dll in dlls:
00342       ident = dll.properties.getProperty("file_path")
00343       self._modules.unregisterObject(ident)
00344     return
00345 
00346 
00347   ##
00348   # @if jp
00349   # @brief モジュールのシンボルの参照
00350   #
00351   # モジュールのシンボルを取得する
00352   #
00353   # @param self
00354   # @param file_name 取得対象ファイル名
00355   # @param func_name 取得対象関数名
00356   #
00357   # @else
00358   # @brief Look up a named symbol in the module
00359   # @endif
00360   def symbol(self, file_name, func_name):
00361     dll = self._modules.find(file_name)
00362     if not dll:
00363       raise ModuleManager.ModuleNotFound, file_name
00364 
00365     func = getattr(dll.dll,func_name,None)
00366 
00367     if not func:
00368       raise ModuleManager.SymbolNotFound, func_name
00369     
00370     return func
00371 
00372 
00373   ##
00374   # @if jp
00375   # @brief モジュールロードパスを指定する
00376   # 
00377   # モジュールロード時に対象モジュールを検索するパスを指定する。
00378   #
00379   # @param self
00380   # @param load_path_list モジュール検索対象パスリスト
00381   #
00382   # @else
00383   # @brief Set default module load path
00384   # @endif
00385   def setLoadpath(self, load_path_list):
00386     self._loadPath = load_path_list
00387     return
00388 
00389 
00390   ##
00391   # @if jp
00392   # @brief モジュールロードパスを取得する
00393   # 
00394   # 設定されているモジュールを検索対象パスリストを取得する。
00395   #
00396   # @param self
00397   # 
00398   # @return load_path モジュール検索対象パスリスト
00399   #
00400   # @else
00401   # @brief Get default module load path
00402   # @endif
00403   def getLoadPath(self):
00404     return self._loadPath
00405 
00406 
00407   ##
00408   # @if jp
00409   # @brief モジュールロードパスを追加する
00410   # 
00411   # 指定されたパスリストを検索対象パスリストに追加する。
00412   #
00413   # @param self
00414   # @param load_path 追加モジュール検索対象パスリスト
00415   #
00416   # @else
00417   # @brief Add module load path
00418   # @endif
00419   def addLoadpath(self, load_path):
00420     for path in load_path:
00421       self._loadPath.append(path)
00422     return
00423 
00424 
00425   ##
00426   # @if jp
00427   # @brief ロード済みのモジュールリストを取得する
00428   #
00429   # 既にロード済みのモジュールリストを取得する。
00430   #
00431   # @param self
00432   #
00433   # @return ロード済みモジュールリスト
00434   #
00435   # @else
00436   # @brief Get loaded module names
00437   # @endif
00438   # std::vector<coil::Properties> getLoadedModules();
00439   def getLoadedModules(self):
00440     dlls = self._modules.getObjects()
00441     modules = []
00442     for dll in dlls:
00443       modules.append(dll.properties)
00444 
00445     return modules
00446 
00447 
00448   def __getRtcProfile(self, fname):
00449     # file name with full path
00450     fullname  = fname
00451     # directory name
00452     dirname   = os.path.dirname(fname)
00453     sys.path.append(dirname)
00454     # basename
00455     basename  = os.path.basename(fname)
00456     # classname
00457     classname  = basename.split(".")[0].lower()
00458 
00459     # loaded profile = old profiles - new profiles
00460     # for old
00461     oldp = self._mgr.getFactoryProfiles()
00462 
00463     # for new
00464     comp_spec_name = classname+"_spec"
00465 
00466     try:
00467       imp_file = __import__(basename.split(".")[0])
00468     except:
00469       return None
00470     comp_spec = getattr(imp_file,comp_spec_name,None)
00471     if not comp_spec:
00472       return None
00473     newp = OpenRTM_aist.Properties(defaults_str=comp_spec)
00474 
00475     profs = []
00476     
00477     exists = False
00478     for i in range(len(oldp)):
00479       if    oldp[i].getProperty("implementation_id") == newp.getProperty("implementation_id") and \
00480             oldp[i].getProperty("type_name") == newp.getProperty("type_name") and \
00481             oldp[i].getProperty("description") == newp.getProperty("description") and \
00482             oldp[i].getProperty("version") == newp.getProperty("version"):
00483         exists = True
00484     if not exists:
00485       profs.append(newp)
00486 
00487         
00488     # loaded component profile have to be one
00489     if len(profs) == 0:
00490       return OpenRTM_aist.Properties()
00491 
00492     if len(profs) > 1:
00493       return None
00494 
00495     return profs[0]
00496 
00497 
00498   ##
00499   # @if jp
00500   # @brief ロード可能モジュールリストを取得する(未実装)
00501   #
00502   # ロード可能なモジュールのリストを取得する。
00503   #
00504   # @param self
00505   #
00506   # @return ロード可能モジュールリスト
00507   #
00508   # @else
00509   # @brief Get loadable module names
00510   # @endif
00511   def getLoadableModules(self):
00512     # getting loadable module file path list.
00513     modules_ = []
00514     for path in self._loadPath:
00515       if path == "":
00516         continue
00517 
00518       flist = glob.glob(path + os.sep + '*.py')
00519       for file in flist:
00520         if file.find("__init__.py") == -1:
00521           modules_.append(file)
00522     
00523     props = []
00524     # getting module properties from loadable modules
00525     for mod_ in modules_:
00526       prop = self.__getRtcProfile(mod_)
00527       if prop:
00528         prop.setProperty("module_file_name",os.path.basename(mod_))
00529         prop.setProperty("module_file_path", mod_)
00530         props.append(prop)
00531 
00532     return props
00533 
00534 
00535 
00536   ##
00537   # @if jp
00538   # @brief モジュールの絶対パス指定許可
00539   #
00540   # ロード対象モジュールの絶対パス指定を許可するように設定する。
00541   #
00542   # @param self
00543   #
00544   # @else
00545   # @brief Allow absolute load path
00546   # @endif
00547   def allowAbsolutePath(self):
00548     self._absoluteAllowed = True
00549 
00550 
00551   ##
00552   # @if jp
00553   # @brief モジュールの絶対パス指定禁止
00554   #
00555   # ロード対象モジュールの絶対パス指定を禁止するように設定する。
00556   #
00557   # @param self
00558   #
00559   # @else
00560   # @brief Forbid absolute load path
00561   # @endif
00562   def disallowAbsolutePath(self):
00563     self._absoluteAllowed = False
00564 
00565 
00566   ##
00567   # @if jp
00568   # @brief モジュールのURL指定許可
00569   #
00570   # ロード対象モジュールのURL指定を許可する。
00571   # 本設定が許可されている場合、モジュールをダウンロードしてロードすることが
00572   # 許可される。
00573   #
00574   # @param self
00575   #
00576   # @else
00577   # @brief Allow module download
00578   # @endif
00579   def allowModuleDownload(self):
00580     self._downloadAllowed = True
00581 
00582 
00583   ##
00584   # @if jp
00585   # @brief モジュールのURL指定禁止
00586   #
00587   # ロード対象モジュールのURL指定を禁止する。
00588   #
00589   # @param self
00590   #
00591   # @else
00592   # @brief Forbid module download
00593   # @endif
00594   def disallowModuleDownload(self):
00595     self._downloadAllowed = False
00596 
00597 
00598   ##
00599   # @if jp
00600   # @brief LoadPath からのファイルの検索
00601   # 
00602   # 指定されたパス内に、指定されたファイルが存在するか確認する。
00603   #
00604   # @param self
00605   # @param fname 検索対象ファイル名
00606   # @param load_path 検索先パスリスト
00607   #
00608   # @return 検索されたファイル名
00609   #
00610   # @else
00611   # @brief Search file from load path
00612   # @endif
00613   def findFile(self, fname, load_path):
00614     file_name = fname
00615     for path in load_path:
00616       if fname.find(".py") == -1:
00617         f = str(path) + os.sep + str(file_name)+".py"
00618       else:
00619         f = str(path)+ os.sep + str(file_name)
00620       if self.fileExist(f):
00621         return f
00622     return ""
00623 
00624 
00625   ##
00626   # @if jp
00627   # @brief ファイルが存在するかどうかのチェック
00628   #
00629   # 指定されたファイルが存在するか確認する。
00630   #
00631   # @param self
00632   # @param filename 存在確認対象ファイル名
00633   #
00634   # @return ファイル存在確認結果(ファイルあり:true,なし:false)
00635   #
00636   # @else
00637   # @brief Check file existance
00638   # @endif
00639   def fileExist(self, filename):
00640     fname = filename
00641     if fname.find(".py") == -1:
00642       fname = str(filename)+".py"
00643 
00644     if os.path.isfile(fname):
00645       return True
00646 
00647     return False
00648 
00649 
00650 
00651   ##
00652   # @if jp
00653   # @brief 初期化関数シンボルを生成する
00654   #
00655   # 初期化関数の名称を組み立てる。
00656   #
00657   # @param self
00658   # @param file_path 初期化対象モジュール名称
00659   #
00660   # @return 初期化関数名称組み立て結果
00661   #
00662   # @else
00663   # @brief Create initialize function symbol
00664   # @endif
00665   def getInitFuncName(self, file_path):
00666     base_name = os.path.basename(file_path)
00667     return str(self._initFuncPrefix)+str(base_name)+str(self._initFuncSuffix)
00668 
00669 
00670 
00671   ##
00672   # @if jp
00673   # @class DLL
00674   # @brief モジュール保持用内部クラス
00675   # @else
00676   #
00677   # @endif
00678   class DLL:
00679     def __init__(self, dll):
00680       self.dll = dll
00681       return
00682 
00683 
00684   class DLLEntity:
00685     def __init__(self,dll,prop):
00686       self.dll = dll
00687       self.properties = prop
00688 
00689 
00690   class DLLPred:
00691     def __init__(self, name=None, factory=None):
00692       self._filepath = name or factory
00693 
00694     def __call__(self, dll):
00695       return self._filepath == dll.properties.getProperty("file_path")


openrtm_aist_python
Author(s): Shinji Kurihara
autogenerated on Thu Aug 27 2015 14:17:28