Как импортировать модуль с полным путем?

Как я могу загрузить модуль Python с учетом его полного пути? Обратите внимание, что файл может находиться где угодно в файловой системе, так как это параметр конфигурации.

Ответ 1

Для Python 3. 5+ используйте:

import importlib.util
spec = importlib.util.spec_from_file_location("module.name", "/path/to/file.py")
foo = importlib.util.module_from_spec(spec)
spec.loader.exec_module(foo)
foo.MyClass()

Для Python 3.3 и 3.4 используйте:

from importlib.machinery import SourceFileLoader

foo = SourceFileLoader("module.name", "/path/to/file.py").load_module()
foo.MyClass()

(хотя это и устарело в Python 3.4.)

Для Python 2 используйте:

import imp

foo = imp.load_source('module.name', '/path/to/file.py')
foo.MyClass()

Существуют эквивалентные вспомогательные функции для скомпилированных файлов Python и библиотек DLL.

Смотрите также http://bugs.python.org/issue21436.

Ответ 2

Преимущество добавления пути к sys.path(с использованием imp) заключается в том, что он упрощает работу при импорте более одного модуля из одного пакета. Например:

import sys
# the mock-0.3.1 dir contains testcase.py, testutils.py & mock.py
sys.path.append('/foo/bar/mock-0.3.1')

from testcase import TestCase
from testutils import RunTests
from mock import Mock, sentinel, patch

Ответ 3

Похоже, вы не хотите специально импортировать файл конфигурации (который содержит много побочных эффектов и дополнительных осложнений), вы просто хотите запустить его и иметь доступ к полученному пространству имен. Стандартная библиотека предоставляет API специально для этого в виде runpy.run_path:

from runpy import run_path
settings = run_path("/path/to/file.py")

Этот интерфейс доступен в Python 2.7 и Python 3.2 +

Ответ 4

Если ваш модуль верхнего уровня не является файлом, но упакован как каталог с помощью __init__.py, то принятое решение почти работает, но не совсем. В Python 3. 5+ необходим следующий код (обратите внимание на добавленную строку, которая начинается с 'sys.modules'):

MODULE_PATH = "/path/to/your/module/__init__.py"
MODULE_NAME = "mymodule"
import importlib
import sys
spec = importlib.util.spec_from_file_location(MODULE_NAME, MODULE_PATH)
module = importlib.util.module_from_spec(spec)
sys.modules[spec.name] = module 
spec.loader.exec_module(module)

Без этой строки при выполнении exec_module он пытается связать относительный импорт в вашем верхнем уровне __init__.py с именем модуля верхнего уровня - в данном случае "mymodule". Но "mymodule" еще не загружен, поэтому вы получите ошибку "SystemError: Родительский модуль" mymodule "не загружен, не может выполнить относительный импорт". Поэтому вам нужно привязать имя, прежде чем загружать его. Причиной этого является фундаментальный инвариант системы относительного импорта: "Инвариантное удержание заключается в том, что если у вас есть sys.modules ['spam'] и sys.modules ['spam.foo'] (как вы сделали бы после вышеупомянутого импорта), последний должен отображаться как атрибут foo первого " , как описано здесь.

Ответ 5

Вы также можете сделать что-то подобное и добавить каталог, в котором находится файл конфигурации, в путь загрузки Python, а затем просто сделать обычный импорт, предполагая, что вы знаете имя файла заранее, в этом случае "конфигурации".

Беспокойно, но он работает.

configfile = '~/config.py'

import os
import sys

sys.path.append(os.path.dirname(os.path.expanduser(configfile)))

import config

Ответ 6

Чтобы импортировать ваш модуль, вам необходимо добавить его каталог в переменную среды, временно или постоянно.

Временно

import sys
sys.path.append("/path/to/my/modules/")
import my_module

Постоянно

Добавив следующую строку в ваш .bashrc файл (в Linux) и excecute source ~/.bashrc в терминале:

export PYTHONPATH="${PYTHONPATH}:/path/to/my/modules/"

Кредит/Источник: saarrrr, еще один вопрос о стеке

Ответ 7

Вы можете использовать

load_source(module_name, path_to_file) 

из imp module.

Ответ 8

Я придумал слегка измененную версию замечательного ответа @SebastianRittau (я думаю, для Python> 3.4), который позволит вам загрузить файл с любым расширением в качестве модуля, используя spec_from_loader вместо spec_from_file_location:

from importlib.util import spec_from_loader, module_from_spec
from importlib.machinery import SourceFileLoader 

spec = spec_from_loader("module.name", SourceFileLoader("module.name", "/path/to/file.py"))
mod = module_from_spec(spec)
spec.loader.exec_module(mod)

Преимущество кодирования пути в явном SourceFileLoader заключается в том, что механизм не будет пытаться определить тип файла по расширению. Это означает, что вы можете загрузить что-то вроде .txt файла, используя этот метод, но вы не можете сделать это с spec_from_file_location без указания загрузчика, потому что .txt отсутствует в importlib.machinery.SOURCE_SUFFIXES.

Ответ 9

Вы имеете в виду загрузку или импорт?

Вы можете манипулировать списком sys.path, указывать путь к вашему модулю, а затем импортировать ваш модуль. Например, данный модуль находится по адресу:

/foo/bar.py

Вы можете сделать:

import sys
sys.path[0:0] = ['/foo'] # puts the /foo directory at the start of your path
import bar

Ответ 10

Вот код, который работает во всех версиях Python: от 2.7-3.5 и, возможно, даже от других.

config_file = "/tmp/config.py"
with open(config_file) as f:
    code = compile(f.read(), config_file, 'exec')
    exec(code, globals(), locals())

Я протестировал его. Это может быть уродливым, но до сих пор это единственное, что работает во всех версиях.

Ответ 11

import sys
sys.path.append('/your/modul/path')

Ответ 12

def import_file(full_path_to_module):
    try:
        import os
        module_dir, module_file = os.path.split(full_path_to_module)
        module_name, module_ext = os.path.splitext(module_file)
        save_cwd = os.getcwd()
        os.chdir(module_dir)
        module_obj = __import__(module_name)
        module_obj.__file__ = full_path_to_module
        globals()[module_name] = module_obj
        os.chdir(save_cwd)
    except:
        raise ImportError

import_file('/home/somebody/somemodule.py')

Ответ 13

Я считаю, что вы можете использовать imp.find_module() и imp.load_module() для загрузки указанного модуля. Вам нужно будет разбить имя модуля на путь, т.е. Если вы хотите загрузить /home/mypath/mymodule.py, вам нужно будет:

imp.find_module('mymodule', '/home/mypath/')

... но это должно быть выполнено.

Ответ 14

Вы можете использовать модуль pkgutil (в частности метод walk_packages), чтобы получить список пакетов в текущем каталоге. Оттуда тривиально использовать механизм importlib для импорта необходимых вам модулей:

import pkgutil
import importlib

packages = pkgutil.walk_packages(path='.')
for importer, name, is_package in packages:
    mod = importlib.import_module(name)
    # do whatever you want with module now, it been imported!

Ответ 15

Создать модуль python test.py

import sys
sys.path.append("<project-path>/lib/")
from tes1 import Client1
from tes2 import Client2
import tes3

Создать модуль python test_check.py

from test import Client1
from test import Client2
from test import test3

Мы можем импортировать импортированный модуль из модуля.

Ответ 16

Это должно работать

path = os.path.join('./path/to/folder/with/py/files', '*.py')
for infile in glob.glob(path):
    basename = os.path.basename(infile)
    basename_without_extension = basename[:-3]

    # http://docs.python.org/library/imp.html?highlight=imp#module-imp
    imp.load_source(basename_without_extension, infile)

Ответ 17

Эта область Python 3.4 кажется чрезвычайно извилистой, чтобы ее понять! Однако с небольшим взломом с использованием кода от Криса Кэллоуэя в качестве запуска мне удалось что-то сделать. Здесь основная функция.

def import_module_from_file(full_path_to_module):
    """
    Import a module given the full path/filename of the .py file

    Python 3.4

    """

    module = None

    try:

        # Get module name and path from full path
        module_dir, module_file = os.path.split(full_path_to_module)
        module_name, module_ext = os.path.splitext(module_file)

        # Get module "spec" from filename
        spec = importlib.util.spec_from_file_location(module_name,full_path_to_module)

        module = spec.loader.load_module()

    except Exception as ec:
        # Simple error printing
        # Insert "sophisticated" stuff here
        print(ec)

    finally:
        return module

Похоже, что используются не устаревшие модули из Python 3.4. Я не претендую на то, чтобы понять, почему, но, похоже, работает изнутри программы. Я нашел решение Криса работать в командной строке, но не изнутри программы.

Ответ 18

Я не говорю, что это лучше, но для полноты я хотел предложить функцию exec, доступную в обоих python 2 и 3. exec позволяет выполнять произвольный код в глобальной области или во внутренней области, предоставляемой в качестве словаря.

Например, если у вас есть модуль, хранящийся в "/path/to/module "с помощью функции foo(), вы можете запустить его, выполнив следующие действия:

module = dict()
with open("/path/to/module") as f:
    exec(f.read(), module)
module['foo']()

Это делает несколько более явным, что вы загружаете код динамически и предоставляете вам дополнительную мощность, например, возможность предоставления настраиваемых встроенных функций.

И если вам нужен доступ через атрибуты, а не ключи, вы можете создать собственный класс dict для глобальных переменных, который обеспечивает такой доступ, например:

class MyModuleClass(dict):
    def __getattr__(self, name):
        return self.__getitem__(name)

Ответ 19

Чтобы импортировать модуль из заданного имени файла, вы можете временно расширить путь и восстановить системный путь в блоке finally ссылка:

filename = "directory/module.py"

directory, module_name = os.path.split(filename)
module_name = os.path.splitext(module_name)[0]

path = list(sys.path)
sys.path.insert(0, directory)
try:
    module = __import__(module_name)
finally:
    sys.path[:] = path # restore

Ответ 20

Импортировать модули пакета во время выполнения (рецепт Python)

http://code.activestate.com/recipes/223972/

###################
##                #
## classloader.py #
##                #
###################

import sys, types

def _get_mod(modulePath):
    try:
        aMod = sys.modules[modulePath]
        if not isinstance(aMod, types.ModuleType):
            raise KeyError
    except KeyError:
        # The last [''] is very important!
        aMod = __import__(modulePath, globals(), locals(), [''])
        sys.modules[modulePath] = aMod
    return aMod

def _get_func(fullFuncName):
    """Retrieve a function object from a full dotted-package name."""

    # Parse out the path, module, and function
    lastDot = fullFuncName.rfind(u".")
    funcName = fullFuncName[lastDot + 1:]
    modPath = fullFuncName[:lastDot]

    aMod = _get_mod(modPath)
    aFunc = getattr(aMod, funcName)

    # Assert that the function is a *callable* attribute.
    assert callable(aFunc), u"%s is not callable." % fullFuncName

    # Return a reference to the function itself,
    # not the results of the function.
    return aFunc

def _get_class(fullClassName, parentClass=None):
    """Load a module and retrieve a class (NOT an instance).

    If the parentClass is supplied, className must be of parentClass
    or a subclass of parentClass (or None is returned).
    """
    aClass = _get_func(fullClassName)

    # Assert that the class is a subclass of parentClass.
    if parentClass is not None:
        if not issubclass(aClass, parentClass):
            raise TypeError(u"%s is not a subclass of %s" %
                            (fullClassName, parentClass))

    # Return a reference to the class itself, not an instantiated object.
    return aClass


######################
##       Usage      ##
######################

class StorageManager: pass
class StorageManagerMySQL(StorageManager): pass

def storage_object(aFullClassName, allOptions={}):
    aStoreClass = _get_class(aFullClassName, StorageManager)
    return aStoreClass(allOptions)

Ответ 21

Я сделал пакет, который использует imp для вас. Я называю это import_file, и так оно и используется:

>>>from import_file import import_file
>>>mylib = import_file('c:\\mylib.py')
>>>another = import_file('relative_subdir/another.py')

Вы можете получить его по адресу:

http://pypi.python.org/pypi/import_file

или

http://code.google.com/p/import-file/

Ответ 22

В Linux добавление символической ссылки в каталог, в котором работает ваш скрипт python, работает.

то есть:

ln -s /absolute/path/to/module/module.py /absolute/path/to/script/module.py

python создаст /absolute/path/to/script/module.pyc и обновит его, если вы измените содержимое /absolute/path/to/module/module.py

затем включите следующее в mypythonscript.py

from module import *

Ответ 23

довольно простой способ: предположим, что вы хотите импортировать файл с относительным путем. /../MyLibs/pyfunc.py


libPath = '../../MyLibs'
import sys
if not libPath in sys.path: sys.path.append(libPath)
import pyfunc as pf

Но если вы сделаете это без охраны, вы, наконец, сможете пройти очень длинный путь

Ответ 24

Простое решение с использованием importlib вместо пакета imp (проверено на Python 2.7, хотя оно должно работать и для Python 3):

import importlib

dirname, basename = os.path.split(pyfilepath) # pyfilepath: '/my/path/mymodule.py'
sys.path.append(dirname) # only directories should be added to PYTHONPATH
module_name = os.path.splitext(basename)[0] # '/my/path/mymodule.py' --> 'mymodule'
module = importlib.import_module(module_name) # name space of defined module (otherwise we would literally look for "module_name")

Теперь вы можете напрямую использовать пространство имен импортированного модуля, например:

a = module.myvar
b = module.myfunc(a)

Преимущество этого решения заключается в том, что нам даже не нужно знать фактическое имя модуля, который мы хотели бы импортировать, чтобы использовать его в нашем коде. Это полезно, например, если путь модуля является настраиваемым аргументом.

Ответ 25

Это может быть очевидно, но в интерактивной оболочке:

cd path
import module

Ответ 26

Добавьте это в список ответов, поскольку я не мог найти ничего, что сработало. Это позволит импортировать скомпилированные (pyd) модули python в 3.4:

import sys
import importlib.machinery

def load_module(name, filename):
    # If the Loader finds the module name in this list it will use
    # module_name.__file__ instead so we need to delete it here
    if name in sys.modules:
        del sys.modules[name]
    loader = importlib.machinery.ExtensionFileLoader(name, filename)
    module = loader.load_module()
    locals()[name] = module
    globals()[name] = module

load_module('something', r'C:\Path\To\something.pyd')
something.do_something()

Ответ 27

Этот ответ является дополнением к ответу Себастьяна Риттау, отвечая на комментарий: "но что, если у вас нет имени модуля?" Это быстрый и грязный способ получить вероятное имя модуля python с именем файла - он просто поднимается по дереву, пока не найдет каталог без файла __init__.py и затем вернет его обратно в имя файла. Для Python 3. 4+ (использует pathlib), что имеет смысл, поскольку пользователи Py2 могут использовать "imp" или другие способы выполнения относительного импорта:

import pathlib

def likely_python_module(filename):
    '''
    Given a filename or Path, return the "likely" python module name.  That is, iterate
    the parent directories until it doesn't contain an __init__.py file.

    :rtype: str
    '''
    p = pathlib.Path(filename).resolve()
    paths = []
    if p.name != '__init__.py':
        paths.append(p.stem)
    while True:
        p = p.parent
        if not p:
            break
        if not p.is_dir():
            break

        inits = [f for f in p.iterdir() if f.name == '__init__.py']
        if not inits:
            break

        paths.append(p.stem)

    return '.'.join(reversed(paths))

Конечно, есть возможности для улучшения, а необязательные файлы __init__.py могут потребовать другие изменения, но если у вас есть __init__.py в целом, это делает трюк.

Ответ 28

Я написал свою собственную глобальную и переносимую функцию импорта для:

  • Уметь импортировать оба модуля как подмодуль и импортировать содержимое модуля в родительский модуль (или в глобальные переменные, если у него нет родительского модуля).
  • Уметь импортировать модули с символами точки в имени файла.
  • Уметь импортировать модули с любым расширением.
  • Иметь возможность использовать автономное имя для подмодуля вместо имени файла без расширения, которое по умолчанию.
  • Уметь определять порядок импорта на основе ранее импортированного модуля, а не зависеть от sys.path или от какого-либо хранилища путей поиска.

Структура каталогов примеров:

<root>
 |
 +- test.py
 |
 +- testlib.py
 |
 +- /std1
 |   |
 |   +- testlib.std1.py
 |
 +- /std2
 |   |
 |   +- testlib.std2.py
 |
 +- /std3
     |
     +- testlib.std3.py

Зависимость и порядок включения:

test.py
  -> testlib.py
    -> testlib.std1.py
      -> testlib.std2.py
    -> testlib.std3.py 

Реализация:

Магазин последних изменений: https://sourceforge.net/p/tacklelib/tacklelib/HEAD/tree/trunk/python/tacklelib/tacklelib.py

test.py:

import os, sys, inspect, copy

SOURCE_FILE = os.path.abspath(inspect.getsourcefile(lambda:0)).replace('\\','/')
SOURCE_DIR = os.path.dirname(SOURCE_FILE)

print("test::SOURCE_FILE: ", SOURCE_FILE)

class TackleGlobalImportModuleState:
  parent_modules = []
  imported_modules = set()
  exec_guards = []

class TackleLocalImportModuleState:
  # key:    variable token
  # value:  (<value>, <value globals>)
  export_globals = {}

# based on:
# https://stackoverflow.com/questions/3589311/get-defining-class-of-unbound-method-object-in-python-3/25959545#25959545
# https://stackoverflow.com/questions/3589311/get-defining-class-of-unbound-method-object-in-python-3/54597033#54597033
#
def tkl_get_method_class(x, from_module = None):
  import inspect

  if inspect.ismethod(x):
    for cls in inspect.getmro(x.__self__.__class__):
      if cls.__dict__.get(x.__name__) is x:
        return cls
    x = x.__func__ # fallback to __qualname__ parsing
  if inspect.isfunction(x):
    cls_name = x.__qualname__.split('.<locals>', 1)[0].rsplit('.', 1)[0]
    #print('tkl_get_method_class:', x, '->', cls_name, '->', inspect.getmodule(x))
    try:
      cls = getattr(inspect.getmodule(x), cls_name)
    except AttributeError:
      cls = x.__globals__.get(cls_name)
    if isinstance(cls, type):
      return cls

  return getattr(x, '__objclass__', None)  # handle special descriptor objects

# DESCRIPTION:
#
#   The function must be expressed separately to correctly resolve base classes
#   copy function to be able to fix 'globals()' only in those methods which are
#   a part of a class been defined from a source module including a base class.
#   And if a base class is a part of a different module than the module which
#   objects being copied from, then what the base class methods would be
#   skipped to fixup the 'globals()'.
#   In other words a derived and a base class could have be defined in
#   different modules and their methods has to be fixed for a correct globals
#   reference if and only if a being copied class has been defined in the
#   module which members are being copied from. If a class has been defined not
#   in the being copied module, then it member functions must be left as is.
#
def tkl_classcopy(x, from_globals, to_globals):
  import inspect

  if not inspect.isclass(x):
    raise Exception('x must a class: ' + type(x))

  cls_copy = type(x.__name__, x.__bases__, dict(x.__dict__))

  if id(from_globals) != id(to_globals):
    # 'dict(...)' to convert from iteratable, based on: https://stackoverflow.com/questions/6586310/how-to-convert-list-of-key-value-tuples-into-dictionary/6586521#6586521
    for key, value in dict(inspect.getmembers(cls_copy)).items():
      if not key.startswith('__'):
        if inspect.isfunction(value):
          member_cls = tkl_get_method_class(value)
          if member_cls in from_globals:
            #print('  tkl_classcopy:', key)
            # globals retarget to the destination globals
            setattr(cls_copy, key, type(value)(value.__code__, to_globals, value.__name__, value.__defaults__, value.__closure__))

  return cls_copy

# to readdress 'globals()' in all functions
def tkl_membercopy(x, from_globals, to_globals):
  if id(from_globals) != id(to_globals):
    import inspect

    if inspect.isfunction(x):
      return type(x)(x.__code__, to_globals, x.__name__, x.__defaults__, x.__closure__)
    elif inspect.isclass(x):
      return tkl_classcopy(x, from_globals, to_globals)

  return x # return by reference

def tkl_merge_module(from_, to):
  import inspect

  from_globals = vars(from_)
  if inspect.ismodule(to):
    to_dict = vars(to)
    to_globals = False
  else:
    to_dict = to
    to_globals = True
  for from_key, from_value in vars(from_).items():
    if not from_key.startswith('__') and from_key not in ['SOURCE_FILE', 'SOURCE_DIR']:
      if from_key in to_dict:
        to_value = to_dict[from_key]
        if id(from_value) != id(to_value):
          if not inspect.ismodule(from_value) and not inspect.ismodule(to_value):
            if not to_globals:
              #print(" tkl_merge_module: ", to.__name__, '<-' , from_key)
              var_copy = tkl_membercopy(from_value, from_globals, to_dict)
              setattr(to, from_key, var_copy)
            else:
              #print(" tkl_merge_module: globals() <- ", from_key)
              to[from_key] = tkl_membercopy(from_value, from_globals, to_dict)
          else:
            tkl_merge_module(from_value, to_value)
      else:
        if not to_globals:
          #print(" tkl_merge_module: ", to.__name__,'<-' ,from_key)
          var_copy = tkl_membercopy(from_value, from_globals, to_dict)
          setattr(to, from_key, var_copy)
        else:
          #print(" tkl_merge_module: globals() <-", from_key)
          to[from_key] = tkl_membercopy(from_value, from_globals, to_dict)

  return to

def tkl_get_parent_imported_module_state(ignore_not_scoped_modules):
  current_globals = globals()
  parent_module = None
  parent_scope_info = {}

  parent_modules = current_globals['TackleGlobalImportModuleState'].parent_modules

  if len(parent_modules) > 0:
    parent_module = None
    for parent_module_tuple in reversed(parent_modules):
      if not ignore_not_scoped_modules or parent_module_tuple[0] != '.':
        parent_module = parent_module_tuple[1]
        parent_scope_info = parent_module_tuple[2]
        parent_members = vars(parent_module)
        break

  if parent_module is None:
    parent_members = current_globals

  return (parent_module, parent_members, parent_scope_info)

# to auto export globals from a parent module to a child module on it import
def tkl_declare_global(var, value, value_from_globals = None, auto_export = True):
  import inspect, copy

  current_globals = globals()
  if value_from_globals is None:
    value_from_globals = current_globals

  # get parent module state
  parent_module, parent_members, parent_scope_info = tkl_get_parent_imported_module_state(False)

  # make a global either in a current module or in the globals
  if not parent_module is None:
    setattr(parent_module, var, value)
  else:
    parent_members[var] = value

  export_globals = current_globals['TackleLocalImportModuleState'].export_globals
  export_globals[var] = (value, value_from_globals)

  if auto_export:
    imported_modules = current_globals['TackleGlobalImportModuleState'].imported_modules
    for key, global_value in current_globals.items():
      if not key.startswith('__') and key not in ['SOURCE_FILE', 'SOURCE_DIR'] and \
        inspect.ismodule(global_value) and global_value in imported_modules: # ignore modules which are not imported by 'tkl_import_module'
        # make a deepcopy with globals retarget to a child module
        var_copy = copy.deepcopy(tkl_membercopy(value, value_from_globals, vars(global_value)))
        setattr(global_value, var, var_copy)

  return value

# ref_module_name:
#   'module'    - either import the module file as 'module' if the module was not imported before or import locally and membercopy the module content into existed one.
#   '.'         - import the module file locally and do membercopy the content of the module either into the parent module if it exists or into globals.
#   '.module'   - the same as 'module' but the module must not be imported before.
#   'module/'   - the same as 'module' but the module must be imported before.
#   'module.'   - the same as 'module' and the module can not be merged in the next 'module' import (some kind of the '.module' behaviour but for the next import).
#   '.module.'  - has meaning of the both above.
#
def tkl_import_module(dir_path, module_file_name, ref_module_name = None, inject_attrs = {}, prefix_exec_module_pred = None, use_exec_guard = True):
  import os, sys, inspect, copy

  if not ref_module_name is None and ref_module_name == '':
    raise Exception('ref_module_name should either be None or not empty string')

  module_file_path = os.path.normcase(os.path.abspath(os.path.join(dir_path, module_file_name))).replace('\\', '/')
  module_name_wo_ext = os.path.splitext(module_file_name)[0]

  print('import :', module_file_path, 'as', module_name_wo_ext if ref_module_name is None else ref_module_name, '->', list(((parent_imported_module_name + '//' + parent_imported_module.__name__) if parent_imported_module_name != parent_imported_module.__name__ else parent_imported_module.__name__) for parent_imported_module_name, parent_imported_module, parent_imported_module_info in TackleGlobalImportModuleState.parent_modules))

  current_globals = globals()
  exec_guards = current_globals['TackleGlobalImportModuleState'].exec_guards

  if use_exec_guard:
    for guard_module_file_path, imported_module in exec_guards:
      if guard_module_file_path == module_file_path.replace('\\', '/'):
        # copy globals to the parent module
        parent_module, parent_members, parent_scope_info = tkl_get_parent_imported_module_state(False)

        if parent_module and 'nomergemodule' in parent_scope_info and parent_scope_info['nomergemodule']:
          raise Exception('attempt to merge the module content to the existed module has been declared as not mergable: ' + parent_module.__name__)

        if not parent_module is None:
          tkl_merge_module(imported_module, parent_module)
        else:
          imported_module_globals = vars(imported_module)
          for key, value in imported_module_globals.items():
            if not key.startswith('__') and key not in ['SOURCE_FILE', 'SOURCE_DIR']:
              if not inspect.ismodule(value) or key not in parent_members or not inspect.ismodule(parent_members[key]):
                #print(' copy: globals()::', key, ' <- ', value)
                parent_members[key] = tkl_membercopy(value, imported_module_globals, current_globals)
              else:
                tkl_merge_module(value, parent_members[key])

        return imported_module

  # get parent module state
  parent_module, parent_members, parent_scope_info = tkl_get_parent_imported_module_state(False)

  module_must_not_exist   = False
  module_must_exist       = False
  nomerge_module          = False

  if not ref_module_name is None and ref_module_name != '.':
    if ref_module_name.startswith('.'): # module must not been imported before by the name
      ref_module_name = ref_module_name[1:]
      module_must_not_exist = True 
    if ref_module_name.endswith('/'):   # module must be already imported by the name
      ref_module_name = ref_module_name[:-1]
      module_must_exist = True
    if ref_module_name.endswith('.'):   # module must not be merged from others (standalone)
      ref_module_name = ref_module_name[:-1]
      nomerge_module = True

    import_module_name = ref_module_name

    if (module_must_not_exist and module_must_exist):
      raise Exception('The module can be imported either as '.module' or as 'module/', but not as both at a time: ' + ref_module_name)
  else:
    import_module_name = module_name_wo_ext

  if ref_module_name != '.': # import to a local namespace?
    if sys.version_info[0] > 3 or sys.version_info[0] == 3 and sys.version_info[1] >= 4:
      import importlib.util, importlib.machinery
      import_spec = importlib.util.spec_from_loader(import_module_name, importlib.machinery.SourceFileLoader(import_module_name, module_file_path))
      imported_module = importlib.util.module_from_spec(import_spec)
      imported_module_globals = vars(imported_module)

      parent_modules = parent_members['TackleGlobalImportModuleState'].parent_modules
      imported_modules = parent_members['TackleGlobalImportModuleState'].imported_modules
      export_globals = current_globals['TackleLocalImportModuleState'].export_globals

      # auto export globals at first
      for key, value_tuple in export_globals.items(): 
        # make a deepcopy with globals retarget to a child module
        setattr(imported_module, key, copy.deepcopy(tkl_membercopy(value_tuple[0], value_tuple[1], imported_module_globals)))

      # inject attributes in being imported module
      imported_module.SOURCE_FILE = module_file_path
      imported_module.SOURCE_DIR = os.path.dirname(module_file_path)

      imported_module.TackleGlobalImportModuleState = parent_members['TackleGlobalImportModuleState']
      imported_module.TackleLocalImportModuleState = copy.deepcopy(current_globals['TackleLocalImportModuleState'])
      imported_module.tkl_get_method_class = parent_members['tkl_get_method_class']
      imported_module.tkl_classcopy = parent_members['tkl_classcopy']
      imported_module.tkl_membercopy = parent_members['tkl_membercopy']
      imported_module.tkl_merge_module = parent_members['tkl_merge_module']
      imported_module.tkl_get_parent_imported_module_state = parent_members['tkl_get_parent_imported_module_state']
      if 'tkl_declare_global' in parent_members:
        imported_module.tkl_declare_global = parent_members['tkl_declare_global']
      imported_module.tkl_import_module = parent_members['tkl_import_module']

      if 'tkl_source_module' in parent_members:
        imported_module.tkl_source_module = parent_members['tkl_source_module']
      for attr, val in inject_attrs.items():
        setattr(imported_module, attr, val)

      is_module_ref_already_exist = False

      # update parent state
      if parent_module:
        # reference a being imported module from a parent module
        if hasattr(parent_module, import_module_name) and inspect.ismodule(getattr(parent_module, import_module_name)):
          is_module_ref_already_exist = True
      else:
        # reference a being imported module from globals
        if import_module_name in parent_members and inspect.ismodule(parent_members[import_module_name]):
          is_module_ref_already_exist = True

      if module_must_not_exist:
        if is_module_ref_already_exist:
          raise Exception('The module reference must not exist as a module before the import: ' + import_module_name)
      elif module_must_exist:
        if not is_module_ref_already_exist:
          raise Exception('The module reference must already exist as a module before the import: ' + import_module_name)

      if parent_module:
        if not is_module_ref_already_exist:
          setattr(parent_module, import_module_name, imported_module)
      else:
        if not is_module_ref_already_exist:
          parent_members[import_module_name] = imported_module

      # remember last parent before the module execution because of recursion
      parent_modules.append((
        import_module_name, imported_module,
        {'nomergemodule' : nomerge_module}
      ))
      imported_modules.add(imported_module)

      try:
        if prefix_exec_module_pred:
          prefix_exec_module_pred(import_module_name, module_file_path, imported_module)

        # before the 'exec_module'
        exec_guards.append((module_file_path, imported_module))

        import_spec.loader.exec_module(imported_module)

      finally:
        parent_modules.pop()

      # copy the module content into already existed module (behaviour by default, do import as '.module' or 'module.' or '.module.' to prevent a merge)
      if is_module_ref_already_exist:
        if parent_module:
          tkl_merge_module(imported_module, getattr(parent_module, import_module_name))
        else:
          tkl_merge_module(imported_module, parent_members[import_module_name])

    else:
      # back compatability
      import imp
      imported_module = imp.load_source(import_module_name, module_file_path)
      parent_members[import_module_name] = imported_module

      # back compatibility: can not be before the imp module exec
      exec_guards.append((module_file_path, imported_module))

  else: # import to the global namespace
    if parent_module and 'nomergemodule' in parent_scope_info and parent_scope_info['nomergemodule']:
      raise Exception('attempt to merge the module content to the existed module has been declared as not mergable: ' + parent_module.__name__)

    if sys.version_info[0] > 3 or sys.version_info[0] == 3 and sys.version_info[1] >= 4:
      import importlib.util, importlib.machinery
      import_spec = importlib.util.spec_from_loader(module_name_wo_ext, importlib.machinery.SourceFileLoader(module_name_wo_ext, module_file_path))
      imported_module = importlib.util.module_from_spec(import_spec)
      imported_module_globals = vars(imported_module)

      parent_modules = parent_members['TackleGlobalImportModuleState'].parent_modules
      imported_modules = parent_members['TackleGlobalImportModuleState'].imported_modules
      export_globals = current_globals['TackleLocalImportModuleState'].export_globals
      parent_export_globals = parent_members['TackleLocalImportModuleState'].export_globals

      # auto export globals at first
      for key, value_tuple in export_globals.items():
        # make a deepcopy with globals retarget to a child module
        setattr(imported_module, key, copy.deepcopy(tkl_membercopy(value_tuple[0], value_tuple[1], imported_module_globals)))

      # inject attributes in being imported module
      imported_module.SOURCE_FILE = module_file_path
      imported_module.SOURCE_DIR = os.path.dirname(module_file_path)

      imported_module.TackleGlobalImportModuleState = parent_members['TackleGlobalImportModuleState']
      imported_module.TackleLocalImportModuleState = copy.deepcopy(current_globals['TackleLocalImportModuleState'])
      imported_module.tkl_get_method_class = parent_members['tkl_get_method_class']
      imported_module.tkl_classcopy = parent_members['tkl_classcopy']
      imported_module.tkl_membercopy = parent_members['tkl_membercopy']
      imported_module.tkl_merge_module = parent_members['tkl_merge_module']
      imported_module.tkl_get_parent_imported_module_state = parent_members['tkl_get_parent_imported_module_state']
      if 'tkl_declare_global' in parent_members:
        imported_module.tkl_declare_global = parent_members['tkl_declare_global']
      imported_module.tkl_import_module = parent_members['tkl_import_module']

      if 'tkl_source_module' in parent_members:
        imported_module.tkl_source_module = parent_members['tkl_source_module']
      for attr, val in inject_attrs.items():
        setattr(imported_module, attr, val)

      # remember last parent before the module execution because of recursion
      parent_modules.append((import_module_name, imported_module, {}))
      imported_modules.add(imported_module)

      try:
        if prefix_exec_module_pred:
          prefix_exec_module_pred(import_module_name, module_file_path, imported_module)

        # before the 'exec_module'
        exec_guards.append((module_file_path, imported_module))

        import_spec.loader.exec_module(imported_module)

      finally:
        last_import_module_state = parent_modules.pop()

      # copy globals to the parent module
      parent_module, parent_members, parent_scope_info = tkl_get_parent_imported_module_state(False)

      if not parent_module is None:
        tkl_merge_module(imported_module, parent_module)
      else:
        imported_module_globals = vars(imported_module) # reget after execution
        for key, value in vars(imported_module).items():
          if not key.startswith('__') and key not in ['SOURCE_FILE', 'SOURCE_DIR']:
            if not inspect.ismodule(value) or key not in parent_members or not inspect.ismodule(parent_members[key]):
              #print(' copy: globals()::', key, ' <- ', value)
              parent_members[key] = tkl_membercopy(value, imported_module_globals, current_globals)
            else:
              tkl_merge_module(value, parent_members[key])

    else:
      # back compatability
      import imp
      imported_module = imp.load_source(module_name_wo_ext, module_file_path).__dict__

      for key, value in imported_module.items():
        if not key.startswith('__') and key not in ['SOURCE_FILE', 'SOURCE_DIR']:
          parent_members[key] = tkl_membercopy(value, current_globals)

      # back compatibility: can not be before the imp module exec
      exec_guards.append((module_file_path, imported_module))


  return imported_module

# shortcut function
def tkl_source_module(dir_path, module_file_name):
  return tkl_import_module(dir_path, module_file_name, '.')

tkl_import_module(SOURCE_DIR, 'testlib.py')

print(globals().keys())

testlib.base_test()
testlib.testlib_std1.std1_test()
testlib.testlib_std1.testlib_std2.std2_test()
#testlib.testlib.std3.std3_test()                             # does not reachable directly ...
getattr(globals()['testlib'], 'testlib.std3').std3_test()     # ... but reachable through the 'globals' + 'getattr'

tkl_import_module(SOURCE_DIR, 'testlib.py', '.')

print(globals().keys())

base_test()
testlib_std1.std1_test()
testlib_std1.testlib_std2.std2_test()
#testlib.std3.std3_test()                                     # does not reachable directly ...
globals()['testlib.std3'].std3_test()                         # ... but reachable through the 'globals' + 'getattr'

testlib.py:

# optional for 3.4.x and higher
#import os, inspect
#
#SOURCE_FILE = os.path.abspath(inspect.getsourcefile(lambda:0)).replace('\\','/')
#SOURCE_DIR = os.path.dirname(SOURCE_FILE)

print("1 testlib::SOURCE_FILE: ", SOURCE_FILE)

tkl_import_module(SOURCE_DIR + '/std1', 'testlib.std1.py', 'testlib_std1')

# SOURCE_DIR is restored here
print("2 testlib::SOURCE_FILE: ", SOURCE_FILE)

tkl_import_module(SOURCE_DIR + '/std3', 'testlib.std3.py')

print("3 testlib::SOURCE_FILE: ", SOURCE_FILE)

def base_test():
  print('base_test')

testlib.std1.py:

# optional for 3.4.x and higher
#import os, inspect
#
#SOURCE_FILE = os.path.abspath(inspect.getsourcefile(lambda:0)).replace('\\','/')
#SOURCE_DIR = os.path.dirname(SOURCE_FILE)

print("testlib.std1::SOURCE_FILE: ", SOURCE_FILE)

tkl_import_module(SOURCE_DIR + '/../std2', 'testlib.std2.py', 'testlib_std2')

def std1_test():
  print('std1_test')

testlib.std2.py:

# optional for 3.4.x and higher
#import os, inspect
#
#SOURCE_FILE = os.path.abspath(inspect.getsourcefile(lambda:0)).replace('\\','/')
#SOURCE_DIR = os.path.dirname(SOURCE_FILE)

print("testlib.std2::SOURCE_FILE: ", SOURCE_FILE)

def std2_test():
  print('std2_test')

testlib.std3.py:

# optional for 3.4.x and higher
#import os, inspect
#
#SOURCE_FILE = os.path.abspath(inspect.getsourcefile(lambda:0)).replace('\\','/')
#SOURCE_DIR = os.path.dirname(SOURCE_FILE)

print("testlib.std3::SOURCE_FILE: ", SOURCE_FILE)

def std3_test():
  print('std3_test')

Вывод (3.7.4):

test::SOURCE_FILE:  <root>/test01/test.py
import : <root>/test01/testlib.py as testlib -> []
1 testlib::SOURCE_FILE:  <root>/test01/testlib.py
import : <root>/test01/std1/testlib.std1.py as testlib_std1 -> ['testlib']
import : <root>/test01/std1/../std2/testlib.std2.py as testlib_std2 -> ['testlib', 'testlib_std1']
testlib.std2::SOURCE_FILE:  <root>/test01/std1/../std2/testlib.std2.py
2 testlib::SOURCE_FILE:  <root>/test01/testlib.py
import : <root>/test01/std3/testlib.std3.py as testlib.std3 -> ['testlib']
testlib.std3::SOURCE_FILE:  <root>/test01/std3/testlib.std3.py
3 testlib::SOURCE_FILE:  <root>/test01/testlib.py
dict_keys(['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__annotations__', '__builtins__', '__file__', '__cached__', 'os', 'sys', 'inspect', 'copy', 'SOURCE_FILE', 'SOURCE_DIR', 'TackleGlobalImportModuleState', 'tkl_membercopy', 'tkl_merge_module', 'tkl_get_parent_imported_module_state', 'tkl_declare_global', 'tkl_import_module', 'TackleSourceModuleState', 'tkl_source_module', 'TackleLocalImportModuleState', 'testlib'])
base_test
std1_test
std2_test
std3_test
import : <root>/test01/testlib.py as . -> []
1 testlib::SOURCE_FILE:  <root>/test01/testlib.py
import : <root>/test01/std1/testlib.std1.py as testlib_std1 -> ['testlib']
import : <root>/test01/std1/../std2/testlib.std2.py as testlib_std2 -> ['testlib', 'testlib_std1']
testlib.std2::SOURCE_FILE:  <root>/test01/std1/../std2/testlib.std2.py
2 testlib::SOURCE_FILE:  <root>/test01/testlib.py
import : <root>/test01/std3/testlib.std3.py as testlib.std3 -> ['testlib']
testlib.std3::SOURCE_FILE:  <root>/test01/std3/testlib.std3.py
3 testlib::SOURCE_FILE:  <root>/test01/testlib.py
dict_keys(['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__annotations__', '__builtins__', '__file__', '__cached__', 'os', 'sys', 'inspect', 'copy', 'SOURCE_FILE', 'SOURCE_DIR', 'TackleGlobalImportModuleState', 'tkl_membercopy', 'tkl_merge_module', 'tkl_get_parent_imported_module_state', 'tkl_declare_global', 'tkl_import_module', 'TackleSourceModuleState', 'tkl_source_module', 'TackleLocalImportModuleState', 'testlib', 'testlib_std1', 'testlib.std3', 'base_test'])
base_test
std1_test
std2_test
std3_test

Если вы хотите загрузить скрипт ввода обычным способом на Python с помощью sys.path, то:

sys.path.append(<path-to-where-test.py-directory-is>)
import test as tkl
# all functions in the module have has a 'tkl_' prefix, all classes begins by 'Tackle', so we don't need a scope here
tkl.tkl_merge_module(tkl, globals())
# cleanup
tkl = None
sys.path.pop()

# from here start using 'tkl_import_module' instead of a usual python import...

Проверено на Python 3.7.4, 3.2.5, 2.7.16

Доводы:

  • Можно импортировать оба модуля как подмодуль и импортировать содержимое модуля в родительский модуль (или в глобальные переменные, если у него нет родительского модуля).
  • Можно импортировать модули с точками в имени файла.
  • Можно импортировать любой модуль расширения из любого модуля расширения.
  • Можно использовать автономное имя для подмодуля вместо имени файла без расширения, которое по умолчанию (например, testlib.std.py как testlib, testlib.blabla.py как testlib_blabla и т.д.).
  • Не зависит от sys.path или от какого-либо хранилища путей поиска.
  • Не требует сохранения/восстановления глобальных переменных, таких как SOURCE_FILE и SOURCE_DIR между вызовами tkl_import_module.
  • [для 3.4.x и выше] Может смешивать пространства имен модуля во вложенных вызовах tkl_import_module (например: named->local->named или local->named->local и т.д.).
  • [для 3.4.x и выше] Может автоматически экспортировать глобальные переменные/функции/классы из того места, где они объявлены, во все дочерние модули, импортированные через tkl_import_module (через функцию tkl_declare_global).

Против:

  • [для 3.3.x и ниже] Требуется объявить tkl_import_module во всех модулях, которые вызывают tkl_import_module (дублирование кода)

Обновление 1,2 (только для 3.4.x и выше):

В Python 3.4 и выше вы можете обойти требование объявить tkl_import_module в каждом модуле, объявив tkl_import_module в модуле верхнего уровня, и функция внедрит себя во все дочерние модули за один вызов (это своего рода импорт самостоятельного развертывания).

Обновление 3:

Добавлена функция tkl_source_module в качестве аналога bash source с поддержкой защиты выполнения при импорте (реализована с помощью слияния модулей вместо импорта).

Обновление 4:

Добавлена функция tkl_declare_global для автоматического экспорта глобальной переменной модуля во все дочерние модули, где глобальная переменная модуля не видна, поскольку не является частью дочернего модуля.

Ответ 29

Лучший способ, я думаю, из официальной документации (29.1. imp - доступ к внутренним внутренним данным):

import imp
import sys

def __import__(name, globals=None, locals=None, fromlist=None):
    # Fast path: see if the module has already been imported.
    try:
        return sys.modules[name]
    except KeyError:
        pass

    # If any of the following calls raises an exception,
    # there a problem we can't handle -- let the caller handle it.

    fp, pathname, description = imp.find_module(name)

    try:
        return imp.load_module(name, fp, pathname, description)
    finally:
        # Since we may exit via an exception, close fp explicitly.
        if fp:
            fp.close()