6.13 筆記法實例 (plugins.py)

import pkgutil
import inspect

from viper.common.abstracts import Module

def load_modules():
    # Import modules package.
    import modules

    plugins = dict()

    # Walk recursively throug hall modules and packages.
    for loader, module_name, ispkg in pkgutil.walk_packages(modules.__path__, modules.__name__ + '.'):
        # If current item is a package, skip.
        if ispkg:
            continue

        # Try to import the module, otherwise skip.
        try:
            module = __import__(module_name, globals(), locals(), ['dummy'], -1)
        except ImportError as e:
            continue

        # Walk through all members of currently imported modules.
        for member_name, member_object in inspect.getmembers(module):
            # Check if current member is a class.
            if inspect.isclass(member_object):
                # Yield the class if it's a subclass of Module.
                if issubclass(member_object, Module) and member_object is not Module:
                    plugins[member_object.cmd] = dict(obj=member_object, description=member_object.description)

    return plugins

__modules__ = load_modules()
plugins.py 裡面主要做了
1. 使用 pkgutil 模組,該模組提供實用 function 以支援 import 系統。
2. 使用 inspect 模組,針對所匯入之模組做一些基本檢查,例如:是否為 class 等。

首先看到 pkgutil 模組,該模組提供走訪特定路徑下 package 的功能。
此處用到的是 pkgutil.walk_packages()
https://docs.python.org/2/library/pkgutil.html

傳入參數通常只會有兩個 (手冊上共三個)
1. path: 走訪模組之路徑
2. prefix: 印出 module name 之前的 prefix,通常為 module.__name__ + '.'

此 function 回傳三個東西
1. module_loader: 尚不知為何物, plugins.py 中沒用到
2. module_name: 用在 __import__ 之參數
3. ispkg: 檢查是否為 package

若被檢查出是個 package,則 continue。
package 與 module 差別如下

A module is a single file (or files) that are imported under one import and used. e.g.
    import my_module

A package is a collection of modules in directories that give a package hierarchy.
    from my_package.timing.danger.internets import function_of_love

http://stackoverflow.com/questions/7948494/whats-the-difference-between-a-python-module-and-a-python-package

若不是,則使用 __import__, 將 module import。

__import__ 為 python 內建函式
傳入值有 name, globals, locals, fromlist, level

name: The function imports the module name

globals, locals: potentially using the given globals and locals 
                 to determine how to interpret the name in a package context.
                 
fromlist: The fromlist gives the names of objects or submodules 
          that should be imported from the module given by name.

level: specifies whether to use absolute or relative imports. 
       The default is -1 which indicates both absolute and relative imports will be attempted.

https://docs.python.org/2/library/functions.html#__import__
手冊範例:
from spam.ham import eggs, sausage as saus

等於
_temp = __import__('spam.ham', globals(), locals(), ['eggs', 'sausage'], -1)
eggs = _temp.eggs
saus = _temp.sausage

從定義與手冊上範例來看

fromlist 中寫 dummy 或許是可省略的?
解答:
是可省略,但得剛好 name 參數的值不是 package.module 形式,若是,則會 return package。
但若 fromlist 不為空值,則會 return module。
dummy 意思就是防範這樣的錯誤發生。

__import__ 後,用 inspect.getmembers() 取得 module 裡 all the members。
function 回傳 member_name 及 member_object。
member_object 會先被檢查是否為 class。
接著再被檢查是否繼承自 Module 且 member_object 不是 Module 本身。
若通過檢查,

就用

plugins[member_object.cmd] = dict(obj=member_object, description=member_object.description)
將相對應指令塞入 dict 的 key 值,並透過建立巢狀字典,將 obj=member_object。
開發者就可透過 module = __modules__[root]['obj']() 初始化模組的 instance。

參考文獻:https://docs.python.org/2/library/inspect.html

Last updated

Was this helpful?