同じインターフェイスをエクスポートしている複数のスクリプトがあり、それらは隔離されたスコープで execfile() を使用して実行されます。
問題は、新しいスクリプトごとに最初からそれらを再度ロードする必要がないように、いくつかのリソースを共有することです。これにより、開始速度が低下し、不要な量の RAM が使用されます。
スクリプトは実際には、以下の例に示すよりもはるかにうまくカプセル化され、悪意のあるプラグインから保護されています。ここから問題が始まります。
問題は、リソースを作成するスクリプトで、データを入力したり、データを削除したり、リソースを削除したり、もちろんそのデータにアクセスしたりできるようにすることです。
ただし、他のスクリプトは、別のスクリプト リソースを変更することはできません。それを読み取るだけです。新しくインストールされたプラグインが、共有リソースの乱用によって、既にロードされ実行されているプラグインに干渉できないようにしたいと考えています。
例:
class SharedResources:
# Here should be a shared resource manager that I tried to write
# but got stuck. That's why I ask this long and convoluted question!
# Some beginning:
def __init__ (self, owner):
self.owner = owner
def __call__ (self):
# Here we should return some object that will do
# required stuff. Read more for details.
pass
class plugin (dict):
def __init__ (self, filename):
dict.__init__(self)
# Here some checks and filling with secure versions of __builtins__ etc.
# ...
self["__name__"] = "__main__"
self["__file__"] = filename
# Add a shared resources manager to this plugin
self["SharedResources"] = SharedResources(filename)
# And then:
execfile(filename, self, self)
# Expose the plug-in interface to outside world:
def __getattr__ (self, a):
return self[a]
def __setattr__ (self, a, v):
self[a] = v
def __delattr__ (self, a):
del self[a]
# Note: I didn't use self.__dict__ because this makes encapsulation easier.
# In future I won't use object itself at all but separate dict to do it. For now let it be
----------------------------------------
# An example of two scripts that would use shared resource and be run with plugins["name"] = plugin("<filename>"):
# Presented code is same in both scripts, what comes after will be different.
def loadSomeResource ():
# Do it here...
return loadedresource
# Then Load this resource if it's not already loaded in shared resources, if it isn't then add loaded resource to shared resources:
shr = SharedResources() # This would be an instance allowing access to shared resources
if not shr.has_key("Default Resources"):
shr.create("Default Resources")
if not shr["Default Resources"].has_key("SomeResource"):
shr["Default Resources"].add("SomeResource", loadSomeResource())
resource = shr["Default Resources"]["SomeResource"]
# And then we use normally resource variable that can be any object.
# Here I Used category "Default Resources" to add and/or retrieve a resource named "SomeResource".
# I want more categories so that plugins that deal with audio aren't mixed with plug-ins that deal with video for instance. But this is not strictly needed.
# Here comes code specific for each plug-in that will use shared resource named "SomeResource" from category "Default Resources".
...
# And end of plugin script!
----------------------------------------
# And then, in main program we load plug-ins:
import os
plugins = {} # Here we store all loaded plugins
for x in os.listdir("plugins"):
plugins[x] = plugin(x)
2 つのスクリプトがプラグイン ディレクトリに格納されており、どちらもメモリにロードされた WAVE ファイルを使用しているとします。最初に読み込まれるプラグインは、WAVE を読み込み、RAM に配置します。他のプラグインは、既にロードされている WAVE にアクセスできますが、それを置き換えたり削除したりすることはできず、他のプラグインを台無しにします。
ここで、各リソースに所有者、プラグイン スクリプトの ID またはファイル名を設定し、このリソースはその所有者だけが書き込みできるようにします。
他のプラグインが最初のプラグインにアクセスできるようにする調整や回避策はありません。
私はほとんどそれをやりましたが、行き詰まりました。私の頭は、実装されたときに部分的にしか機能しないという概念でぐるぐる回っています。これは私を食べてしまうので、これ以上集中できません。どんな提案でも大歓迎です!
追加:
これは、安全性を含めずに現在使用しているものです。
# Dict that will hold a category of resources (should implement some security):
class ResourceCategory (dict):
def __getattr__ (self, i): return self[i]
def __setattr__ (self, i, v): self[i] = v
def __delattr__ (self, i): del self[i]
SharedResources = {} # Resource pool
class ResourceManager:
def __init__ (self, owner):
self.owner = owner
def add (self, category, name, value):
if not SharedResources.has_key(category):
SharedResources[category] = ResourceCategory()
SharedResources[category][name] = value
def get (self, category, name):
return SharedResources[category][name]
def rem (self, category, name=None):
if name==None: del SharedResources[category]
else: del SharedResources[category][name]
def __call__ (self, category):
if not SharedResources.has_key(category):
SharedResources[category] = ResourceCategory()
return SharedResources[category]
__getattr__ = __getitem__ = __call__
# When securing, this must not be left as this, it is unsecure, can provide a way back to SharedResources pool:
has_category = has_key = SharedResources.has_key
プラグイン カプセル:
class plugin(dict):
def __init__ (self, path, owner):
dict.__init__()
self["__name__"] = "__main__"
# etc. etc.
# And when adding resource manager to the plugin, register it with this plugin as an owner
self["SharedResources"] = ResourceManager(owner)
# ...
execfile(path, self, self)
# ...
プラグイン スクリプトの例:
#-----------------------------------
# Get a category we want. (Using __call__() ) Note: If a category doesn't exist, it is created automatically.
AudioResource = SharedResources("Audio")
# Use an MP3 resource (let say a bytestring):
if not AudioResource.has_key("Beep"):
f = open("./sounds/beep.mp3", "rb")
Audio.Beep = f.read()
f.close()
# Take a reference out for fast access and nicer look:
beep = Audio.Beep # BTW, immutables doesn't propagate as references by themselves, doesn't they? A copy will be returned, so the RAM space usage will increase instead. Immutables shall be wrapped in a composed data type.
これは完全に機能しますが、前述したように、リソースを台無しにするのは簡単すぎます。
ResourceManager() のインスタンスが、保存されたデータのどのバージョンを返すかを担当したいと思います。