有条件映射的模块导入

时间:2018-07-01 20:20:37

标签: python python-2.7 python-import

我目前正在为Python 2.7开发一组抽象模块,我将以python软件包的形式提供:

class MyView extends Polymer.Element{
  static get is() {return 'my-view';}
  static get properties() {
    return{
      data:{
        type: String,
        value: 'default',
        observer: 'dataChanged',
      },
    };
  }

  storeData() {
    this.data = 'stored';
  }

  dataChanged(data, oldData) {
    console.log('dataChanged:', data, oldData);

    // TODO: Skip default dataChanged when data is stored.
  }
}

然后,将在完全不同的一组软件包中实现这些模块:

 myabstractpkg
   - abstract
      - core
       - logging
       - ...
     - nodes
     - ...

但是,在运行时,我希望能够始终在使用已实现模块的工具中进行这样的导入:

 myimppkg
   - implementation
     - core
       - logging
       - ...
     - nodes
     - ...

通过这种方式,开发人员始终从“虚拟” api模块导入,然后由该模块确定实际指向导入程序的位置。

我知道我可以通过修改模块字典将其合并到一起:

from myabstractpkg.api import nodes
from myabstractpkg.api.core.logging import Logger

或者为from myimppkg import implementation sys.modules["myabstractpkg.api"] = implementation __init__.py中的所有内容进行巧妙的导入,但这对我来说有点脆弱。

我想知道你们是否对实现此目标的最佳方法有一些建议。在整个重新映射模块方面,我可能会走得很糟,因此,如果你们有任何更智能,更Python化的解决方案,那么对于我的API抽象,实现,使用方法,我希望能听到它们。

1 个答案:

答案 0 :(得分:1)

我相信,最好利用entry_points中的setuptools capabilities为您服务。因此,在您的一种具体实现的setup.py中,您将像这样定义entry_points

setup(
    name="concrete_extension"
    entry_points={
        "abstract_pkg_extensions": [
            "concrete = concrete_extension"
        ]
    }
}

然后,您可以在抽象包中包含一个扩展模块,其功能如下:

import pkg_resources
import os
import sys

from . import default

extensions = { "default": default }
extensions.update({e.name: e.load() for e in 
pkg_resources.iter_entry_points("my_pkg_extensions")})

current_implementation_name = None
current_implementation = None

def set_implementation(name):
    global current_implementation_name, current_implementation
    try:
        current_implementation = extensions[name]
        current_implementation_name = name

        # allow imports like from foo.current_implementation.bar import baz
        sys.modules["{}.current_implementation".format(__name__)] = current_implementation
        # here is where you would do any additional stuff
        # -- e.g. --
        # from . import logger
        # logger.Logger = current_implementation.Logger
    except KeyError:
        raise NotImplementedError("No implementation for: {}".format(name))

set_implementation(os.environ.get("CURRENT_IMPLEMENTATION_NAME", "default"))

然后,您可以使用ext.current_implementation访问当前实现,并在导入之前在程序环境中设置实现,或者可以在导入任何子模块之前在代码中显式调用set_implementation使用ext.current_implementation

有关sys.modules条目及其详细工作方式的更多信息,请参见this question