¿Cómo cargo un módulo de Python dada su ruta completa?

Tenga en cuenta que el archivo puede estar en cualquier parte del sistema de archivos.

respuesta

Para el uso de Python 3.5+ ( docs ):

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()

Para usar Python 3.3 y 3.4:

from importlib.machinery import SourceFileLoader

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

(Aunque esto ha quedado obsoleto en Python 3.4).

Para el uso de Python 2:

import imp

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

Hay funciones de conveniencia equivalentes para archivos Python compilados y DLL.

Consulte también http://bugs.python.org/issue21436 .

La ventaja de agregar una ruta a sys.path (en lugar de usar imp) es que simplifica las cosas al importar más de un módulo de un solo paquete. Por ejemplo:

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

Para importar su módulo, debe agregar su directorio a la variable de entorno, ya sea de forma temporal o permanente.

Temporalmente

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

Permanentemente

Agregar la siguiente línea a su .bashrcarchivo (o alternativa) en Linux y ejecutar source ~/.bashrc(o alternativa) en la terminal:

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

Crédito/Fuente: saarrrr , otra pregunta de Stack Exchange

Si su módulo de nivel superior no es un archivo sino que está empaquetado como un directorio con __init__.py, entonces la solución aceptada casi funciona, pero no del todo. En Python 3.5+ se necesita el siguiente código (tenga en cuenta la línea agregada que comienza con '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)

Sin esta línea, cuando se ejecuta exec_module, intenta vincular las importaciones relativas en su nivel superior __init__.py al nombre del módulo de nivel superior, en este caso, "mymodule". Pero "mymodule" aún no está cargado, por lo que obtendrá el error "SystemError: el módulo principal 'mymodule' no está cargado, no puede realizar una importación relativa". Por lo tanto, debe vincular el nombre antes de cargarlo. La razón de esto es el invariante fundamental del sistema de importación relativa: "La propiedad invariable es que si tiene sys.modules['spam'] y sys.modules['spam.foo'] (como lo haría después de la importación anterior ), el último debe aparecer como el atributo foo del primero" como se analiza aquí .

Parece que no desea importar específicamente el archivo de configuración (que tiene muchos efectos secundarios y complicaciones adicionales). Solo desea ejecutarlo y poder acceder al espacio de nombres resultante. La biblioteca estándar proporciona una API específicamente para eso en forma de runpy.run_path :

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

Esa interfaz está disponible en Python 2.7 y Python 3.2+.

También puede hacer algo como esto y agregar el directorio en el que se encuentra el archivo de configuración a la ruta de carga de Python, y luego simplemente hacer una importación normal, suponiendo que conoce el nombre del archivo de antemano, en este caso "config".

Desordenado, pero funciona.

configfile = '~/config.py'

import os
import sys

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

import config

Puedes usar el

load_source(module_name, path_to_file)

método del módulo imp .

¿Te refieres a cargar o importar?

Puede manipular la sys.pathlista, especificar la ruta a su módulo y luego importar su módulo. Por ejemplo, dado un módulo en:

/foo/bar.py

Podrías hacerlo:

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

Aquí hay un código que funciona en todas las versiones de Python, desde 2.7-3.5 y probablemente incluso otras.

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

Lo probé. Puede que sea feo, pero hasta el momento es el único que funciona en todas las versiones.

Se me ocurrió una versión ligeramente modificada de la maravillosa respuesta de @SebastianRittau (creo que para Python> 3.4), que le permitirá cargar un archivo con cualquier extensión como módulo usando spec_from_loaderen lugar de 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)

La ventaja de codificar la ruta de forma explícita SourceFileLoaderes que la maquinaria no intentará averiguar el tipo de archivo a partir de la extensión. Esto significa que puede cargar algo como un .txtarchivo usando este método, pero no podría hacerlo spec_from_file_locationsin especificar el cargador porque .txtno está en formato importlib.machinery.SOURCE_SUFFIXES.

Coloqué una implementación basada en esto, y la modificación útil de @SamGrondahl en mi biblioteca de utilidades, haggis . haggis.load.load_moduleLa función se llama Agrega un par de buenos trucos, como la capacidad de inyectar variables en el espacio de nombres del módulo a medida que se carga.

Puedes hacer esto usando __import__y chdir:

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 Exception as e:
        raise ImportError(e)
    return module_obj


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

Si tenemos scripts en el mismo proyecto pero en diferentes medios de directorio, podemos resolver este problema con el siguiente método.

En esta situación utils.pyestá ensrc/main/util/

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

import src.main.util.utils
#or
from src.main.util.utils import json_converter # json_converter is example method

Creo que puedes usar imp.find_module()y imp.load_module()para cargar el módulo especificado. Deberá separar el nombre del módulo de la ruta, es decir, si desea cargar /home/mypath/mymodule.py, deberá hacer lo siguiente:

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

... pero eso debería hacer el trabajo.

Puede usar el pkgutilmódulo (específicamente el walk_packagesmétodo) para obtener una lista de los paquetes en el directorio actual. A partir de ahí, es trivial usar la importlibmaquinaria para importar los módulos que desee:

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's been imported!

Hay un paquete que está dedicado específicamente a esto:

from thesmuggler import smuggle

# À la `import weapons`
weapons = smuggle('weapons.py')

# À la `from contraband import drugs, alcohol`
drugs, alcohol = smuggle('drugs', 'alcohol', source='contraband.py')

# À la `from contraband import drugs as dope, alcohol as booze`
dope, booze = smuggle('drugs', 'alcohol', source='contraband.py')

Se ha probado en todas las versiones de Python (Jython y PyPy también), pero puede ser excesivo según el tamaño de su proyecto.

Cree el módulo de Python test.py :

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

Cree el módulo de Python test_check.py :

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

Podemos importar el módulo importado desde module.

¡Esta área de Python 3.4 parece ser extremadamente tortuosa de entender! Sin embargo, con un poco de piratería usando el código de Chris Calloway como comienzo, logré que algo funcionara. Aquí está la función básica.

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

Esto parece usar módulos no obsoletos de Python 3.4. No pretendo entender por qué, pero parece funcionar desde dentro de un programa. Encontré que la solución de Chris funcionó en la línea de comando pero no desde dentro de un programa.

Hice un paquete que usa imppara ti. Lo llamo import_filey así es como se usa:

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

Puedes conseguirlo en:

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

o en

http://code.google.com/p/importar-archivo/

Para importar un módulo desde un nombre de archivo determinado, puede ampliar temporalmente la ruta y restaurar la ruta del sistema en la referencia de bloque final:

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

Una solución simple usando importliben lugar del imppaquete (probado para Python 2.7, aunque también debería funcionar para 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")

Ahora puede usar directamente el espacio de nombres del módulo importado, así:

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

La ventaja de esta solución es que ni siquiera necesitamos saber el nombre real del módulo que nos gustaría importar para poder usarlo en nuestro código. Esto es útil, por ejemplo, en caso de que la ruta del módulo sea un argumento configurable.

No digo que sea mejor, pero en aras de la exhaustividad, quería sugerir la execfunción, disponible tanto en Python 2 como en Python 3.

execle permite ejecutar código arbitrario en el ámbito global o en un ámbito interno, proporcionado como un diccionario.

Por ejemplo, si tiene un módulo almacenado en "/path/to/module" con la función foo(), puede ejecutarlo haciendo lo siguiente:

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

Esto hace que sea un poco más explícito que está cargando el código dinámicamente y le otorga un poder adicional, como la capacidad de proporcionar componentes personalizados.

Y si tener acceso a través de atributos, en lugar de claves, es importante para usted, puede diseñar una clase de dictado personalizada para los globales, que proporciona dicho acceso, por ejemplo:

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

Para agregar a la respuesta de Sebastian Rittau : al menos para CPython , hay pydoc y, aunque no se declara oficialmente, lo que hace es importar archivos:

from pydoc import importfile
module = importfile('/path/to/module.py')

PD. En aras de la exhaustividad, hay una referencia a la implementación actual en el momento de escribir: pydoc.py , y me complace decir que, en la línea de xkcd 1987 , no usa ninguna de las implementaciones mencionadas en el problema 21436 , en menos, no textualmente.

Esto debería funcionar

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)

Importar módulos de paquetes en tiempo de ejecución (receta de 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)

En Linux, funciona agregar un enlace simbólico en el directorio en el que se encuentra la secuencia de comandos de Python.

Es decir:

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

El intérprete de Python lo creará /absolute/path/to/script/module.pycy lo actualizará si cambia el contenido de /absolute/path/to/module/module.py.

Luego incluya lo siguiente en el archivo mypythonscript.py :

from module import *

Esto permitirá la importación de módulos de Python compilados (pyd) en 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()

Una forma bastante simple: suponga que desea importar un archivo con una ruta relativa ../../MyLibs/pyfunc.py

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

Pero si lo haces sin guardia, finalmente puedes obtener un camino muy largo.

He escrito mi propia función de importación global y portátil, basada en el importlibmódulo, para:

  • Ser capaz de importar ambos módulos como submódulos e importar el contenido de un módulo a un módulo principal (o a un módulo global si no tiene un módulo principal).
  • Ser capaz de importar módulos con caracteres de punto en un nombre de archivo.
  • Poder importar módulos con cualquier extensión.
  • Ser capaz de usar un nombre independiente para un submódulo en lugar de un nombre de archivo sin extensión que es el predeterminado.
  • Ser capaz de definir el orden de importación en función del módulo importado previamente en lugar de depender de sys.pathun almacenamiento de ruta de búsqueda.

La estructura del directorio de ejemplos:

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

Dependencia y orden de inclusión:

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

Implementación:

Tienda de cambios más reciente: https://sourceforge.net/p/tacklelib/tacklelib/HEAD/tree/trunk/python/tacklelib/tacklelib.py

prueba.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)

# portable import to the global space
sys.path.append(TACKLELIB_ROOT) # TACKLELIB_ROOT - path to the library directory
import tacklelib as tkl

tkl.tkl_init(tkl)

# cleanup
del tkl # must be instead of `tkl = None`, otherwise the variable would be still persist
sys.path.pop()

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')

Salida ( 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

Probado en Python 3.7.4, 3.2.5,2.7.16

Ventajas :

  • Puede importar ambos módulos como un submódulo y puede importar el contenido de un módulo a un módulo principal (o a un módulo global si no tiene un módulo principal).
  • Puede importar módulos con puntos en un nombre de archivo.
  • Puede importar cualquier módulo de extensión desde cualquier módulo de extensión.
  • Puede usar un nombre independiente para un submódulo en lugar de un nombre de archivo sin extensión que es el predeterminado (por ejemplo, testlib.std.pycomo testlib, testlib.blabla.pycomo , testlib_blablaetc.).
  • No depende de un sys.patho de un almacenamiento de ruta de búsqueda.
  • No requiere guardar/restaurar variables globales como SOURCE_FILEy SOURCE_DIRentre llamadas a tkl_import_module.
  • [para 3.4.xy superior] Puede mezclar los espacios de nombres del módulo en tkl_import_modulellamadas anidadas (por ejemplo, named->local->namedo local->named->local, etc.).
  • [para 3.4.xy superior] Puede exportar automáticamente variables/funciones/clases globales desde donde se declaran a todos los módulos secundarios importados a través de tkl_import_module(a través de la tkl_declare_globalfunción).

Contras :

  • [para 3.3.xe inferiores] Requerir declarar tkl_import_moduleen todos los módulos que llama a tkl_import_module(duplicación de código)

Actualización 1,2 ( 3.4.xsolo para versiones superiores):

En Python 3.4 y versiones posteriores, puede omitir el requisito de declarar tkl_import_moduleen cada módulo declarando tkl_import_moduleen un módulo de nivel superior y la función se inyectaría en todos los módulos secundarios en una sola llamada (es una especie de importación de implementación automática).

Actualización 3 :

Función agregada tkl_source_modulecomo análogo a bash sourcecon protección de ejecución de soporte al importar (implementada a través de la fusión del módulo en lugar de la importación).

Actualización 4 :

Función agregada tkl_declare_globalpara exportar automáticamente una variable global de módulo a todos los módulos secundarios donde una variable global de módulo no es visible porque no es parte de un módulo secundario.

Actualización 5 :

Todas las funciones se han movido a la biblioteca de tacklelib, vea el enlace de arriba.

Estas son mis dos funciones de utilidad que usan solo pathlib. Infiere el nombre del módulo a partir de la ruta.

De forma predeterminada, carga recursivamente todos los archivos de Python desde las carpetas y reemplaza init .py por el nombre de la carpeta principal. Pero también puede dar una ruta y/o un globo para seleccionar algunos archivos específicos.

from pathlib import Path
from importlib.util import spec_from_file_location, module_from_spec
from typing import Optional


def get_module_from_path(path: Path, relative_to: Optional[Path] = None):
    if not relative_to:
        relative_to = Path.cwd()

    abs_path = path.absolute()
    relative_path = abs_path.relative_to(relative_to.absolute())
    if relative_path.name == "__init__.py":
        relative_path = relative_path.parent
    module_name = ".".join(relative_path.with_suffix("").parts)
    mod = module_from_spec(spec_from_file_location(module_name, path))
    return mod


def get_modules_from_folder(folder: Optional[Path] = None, glob_str: str = "*/**/*.py"):
    if not folder:
        folder = Path(".")

    mod_list = []
    for file_path in sorted(folder.glob(glob_str)):
        mod_list.append(get_module_from_path(file_path))

    return mod_list

Aquí hay una forma de cargar archivos como C, etc.

from importlib.machinery import SourceFileLoader
import os

def LOAD(MODULE_PATH):
    if (MODULE_PATH[0] == "/"):
        FULL_PATH = MODULE_PATH;
    else:
        DIR_PATH = os.path.dirname (os.path.realpath (__file__))
        FULL_PATH = os.path.normpath (DIR_PATH + "/" + MODULE_PATH)

    return SourceFileLoader (FULL_PATH, FULL_PATH).load_module ()

Implementaciones donde:

Y = LOAD("../Z.py")
A = LOAD("./A.py")
D = LOAD("./C/D.py")
A_ = LOAD("/IMPORTS/A.py")

Y.DEF();
A.DEF();
D.DEF();
A_.DEF();

Donde cada uno de los archivos se ve así:

def DEF():
    print("A");