diff --git a/direct/src/ffi/panda3d.py b/direct/src/ffi/panda3d.py new file mode 100644 index 0000000000..c85073c059 --- /dev/null +++ b/direct/src/ffi/panda3d.py @@ -0,0 +1,202 @@ +#!/bin/true +import os, sys, imp + +panda3d_modules = { + "core" :("libpandaexpress", "libpanda"), + "physics" : "libpandaphysics", + "fx" : "libpandafx", + "direct" : "libp3direct", + "skel" : "libpandaskel", + "egg" : "libpandaegg", + "ode" : "libpandaode", +} + +class panda3d_import_manager: + # Important: store a reference to the sys and os modules, as + # all references in the global namespace will be reset. + os = os + sys = sys + imp = imp + + # Figure out the dll suffix (commonly, _d for windows debug builds), + # and the dll extension. + dll_suffix = '' + dll_exts = ('.pyd', '.so') + if sys.platform == "win32": + dll_exts = ('.pyd', '.dll') + + # We allow the caller to preload dll_suffix into the sys module. + dll_suffix = getattr(sys, 'dll_suffix', None) + + if dll_suffix is None: + # Otherwise, we try to determine it from the executable name: + # python_d.exe implies _d across the board. + dll_suffix = '' + if sys.executable.endswith('_d.exe'): + dll_suffix = '_d' + + # On OSX, extension modules can be loaded from either .so or .dylib. + if sys.platform == "darwin": + dll_exts = ('.pyd', '.so', '.dylib') + + prepared = False + + @classmethod + def __prepare(cls): + # This method only needs to be called once. + if cls.prepared: + return + cls.prepared = True + + # First, we must ensure that the library path is + # modified to locate all of the dynamic libraries. + target = None + filename = "libpandaexpress" + cls.dll_suffix + if cls.sys.platform == "win32": + filename += "_d" + for dir in cls.sys.path + [cls.sys.prefix]: + lib = cls.os.path.join(dir, filename) + for dll_ext in cls.dll_exts: + if (cls.os.path.exists(lib + dll_ext)): + target = dir + break + if target == None: + message = "Cannot find %s" % (filename) + raise ImportError, message + target = cls.os.path.abspath(target) + + # And add that directory to the system library path. + if cls.sys.platform == "win32": + cls.__prepend_to_path("PATH", target) + else: + cls.__prepend_to_path("LD_LIBRARY_PATH", target) + + if cls.sys.platform == "darwin": + cls.__prepend_to_path("DYLD_LIBRARY_PATH", target) + + @classmethod + def __prepend_to_path(cls, varname, target): + """ Prepends the given directory to the + specified search path environment variable. """ + + # Get the current value + if varname in cls.os.environ: + path = cls.os.environ[varname].strip(cls.os.pathsep) + else: + path = "" + + # Prepend our value, if it's not already the first thing + if len(path) == 0: + cls.os.environ[varname] = target + elif not path.startswith(target): + cls.os.environ[varname] = target + cls.os.pathsep + path + + @classmethod + def libimport(cls, name): + """ Imports and returns the specified library name. The + provided library name has to be without dll extension. """ + + if not cls.prepared: cls.__prepare() + + # Try to import it normally first. + try: + return __import__(name) + except ImportError, err: + if str(err) != "No module named " + name: + raise + + # Hm, importing normally didn't work. Let's try imp.load_dynamic. + # But first, locate the desired library. + target = None + filename = name + cls.dll_suffix + for dir in cls.sys.path + [cls.sys.prefix]: + lib = cls.os.path.join(dir, filename) + for dll_ext in cls.dll_exts: + if (cls.os.path.exists(lib + dll_ext)): + target = lib + dll_ext + break + if target == None: + message = "DLL loader cannot find %s." % name + raise ImportError, message + target = cls.os.path.abspath(target) + + # Now import the file explicitly. + return cls.imp.load_dynamic(name, target) + +class panda3d_submodule(type(sys)): + """ Represents a submodule of 'panda3d' that represents a dynamic + library. This dynamic library is loaded when something is accessed + from the module. """ + + __manager__ = panda3d_import_manager + + def __init__(self, name, library): + type(sys).__init__(self, "panda3d." + name) + self.__library__ = library + self.__libraries__ = [self.__library__] + + def __getattr__(self, name): + mod = self.__manager__.libimport(self.__library__) + if name == "__all__": + return dir(mod) + elif name in dir(mod): + return mod.__dict__[name] + + # Not found? Raise the error that Python would normally raise. + raise AttributeError, "'module' object has no attribute '%s'" % name + +class panda3d_multisubmodule(type(sys)): + """ Represents a submodule of 'panda3d' that represents multiple + dynamic libraries. These are loaded when something is accessed + from the module. """ + + __manager__ = panda3d_import_manager + + def __init__(self, name, libraries): + type(sys).__init__(self, "panda3d." + name) + self.__libraries__ = libraries + + def __getattr__(self, name): + if name == "__all__": + everything = [] + for lib in self.__libraries__: + everything += dir(self.__manager__.libimport(self.__libraries__)) + + for lib in self.__libraries__: + mod = self.__manager__.libimport(lib) + if name in dir(mod): + return mod.__dict__[name] + + # Not found? Raise the error that Python would normally raise. + raise AttributeError, "'module' object has no attribute '%s'" % name + +class panda3d_module(type(sys)): + """ Represents the main 'panda3d' module. """ + + modules = panda3d_modules + + def __getattr__(self, name): + if name == "__all__": + return self.modules.keys() + elif name in self.modules: + return sys.modules["panda3d.%s" % name] + + # Not found? Raise the error that Python would normally raise. + raise AttributeError, "'module' object has no attribute '%s'" % name + +# Create the fake module objects and insert them into sys.modules. +this = panda3d_module("panda3d") + +# Loop through the module dictionary, create a fake +# module for each of them, and insert them into +# sys.modules and into the 'panda3d' fake module. +for mod, lib in panda3d_modules.items(): + if isinstance(lib, tuple): + module = panda3d_multisubmodule(mod, lib) + else: + module = panda3d_submodule(mod, lib) + sys.modules["panda3d." + mod] = module + this.__dict__[mod] = module + +# Important: this must be the last thing in this file +sys.modules["panda3d"] = this