web-dev-qa-db-fra.com

Importez un module python sans l'extension .py

J'ai un fichier appelé foobar (sans extension .py). Dans le même répertoire, j'ai un autre fichier python qui essaie de l'importer:

import foobar

Mais cela ne fonctionne que si je renomme le fichier en foobar.py. Est-il possible d'importer un module python qui n'a pas l'extension .py?

Mise à jour: le fichier n'a pas d'extension car je l'utilise également comme script autonome, et je ne veux pas taper l'extension .py pour l'exécuter.

Update2: Je vais opter pour la solution de lien symbolique mentionnée ci-dessous.

66
compie

Vous pouvez utiliser le imp.load_source fonction (du module imp), pour charger dynamiquement un module à partir d'un chemin de système de fichiers donné.

foobar = imp.load_source('foobar', '/path/to/foobar')

Cette discussion SO montre également quelques options intéressantes.

40
Eli Bendersky

Comme d'autres l'ont mentionné, vous pouvez utiliser imp.load_source, mais cela rendra votre code plus difficile à lire. Je ne le recommanderais vraiment que si vous devez importer des modules dont les noms ou les chemins ne sont connus qu'au moment de l'exécution.

Quelle est la raison pour laquelle vous ne souhaitez pas utiliser l'extension .py? Le cas le plus courant pour ne pas vouloir utiliser l'extension .py est que le script python est également exécuté en tant qu'exécutable, mais vous voulez toujours que d'autres modules puissent l'importer. Si cela Dans ce cas, il peut être avantageux de déplacer des fonctionnalités dans un fichier .py portant un nom similaire, puis d'utiliser foobar comme wrapper.

15
user297250

imp.load_source(module_name, path) devrait faire ou vous pouvez faire la route imp.load_module(module_name, file_handle, ...) plus verbeuse si vous avez un handle de fichier à la place

14
Daniel DiPaolo

Voici une solution pour Python 3.4+:

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

spec = spec_from_loader("foobar", SourceFileLoader("foobar", "/path/to/foobar"))
foobar = module_from_spec(spec)
spec.loader.exec_module(foobar)

En utilisant spec_from_loader et en spécifiant explicitement un SourceFileLoader forcera le machinerie à charger le fichier en tant que source, sans essayer de comprendre le type du fichier de l'extension. Cela signifie que vous pouvez charger le fichier même s'il n'est pas répertorié dans importlib.machinery.SOURCE_SUFFIXES .

Si vous souhaitez continuer à importer le fichier par nom après le premier chargement, ajoutez le module à sys.modules:

sys.modules['foobar'] = foobar
12
Mad Physicist

Si vous installez le script avec le gestionnaire de paquets (deb ou similaire), une autre option serait d'utiliser setuptools:

"... il n'y a pas de moyen simple pour que le nom de fichier d'un script corresponde aux conventions locales sur les plates-formes Windows et POSIX. Pour un autre, vous devez souvent créer un fichier séparé juste pour le script" principal ", lorsque votre" principal "réel est un fonctionner dans un module quelque part ... setuptools corrige tous ces problèmes en générant automatiquement des scripts pour vous avec l'extension correcte, et sur Windows, il créera même un fichier .exe ... "

https://pythonhosted.org/setuptools/setuptools.html#automatic-script-creation

1
user2745509

importlib fonction d'assistance

Voici un assistant pratique et prêt à l'emploi pour remplacer imp, par un exemple, basé sur ce qui a été mentionné à: https://stackoverflow.com/a/43602645/895245 =

main.py

#!/usr/bin/env python3

import os
import importlib

def import_path(path):
    module_name = os.path.basename(path).replace('-', '_')
    spec = importlib.util.spec_from_loader(
        module_name,
        importlib.machinery.SourceFileLoader(module_name, path)
    )
    module = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(module)
    sys.modules[module_name] = module
    return module

notmain = import_path('not-main')
print(notmain)
print(notmain.x)

pas-principal

x = 1

Courir:

python3 main.py

Production:

<module 'not_main' from 'not-main'>
1

Je remplace - avec _ parce que mes exécutables importables Python sans extension ont des tirets. Ce n'est pas obligatoire, mais produit de meilleurs noms de module.

Ce modèle est également mentionné dans les documents à: https://docs.python.org/3.7/library/importlib.html#importing-a-source-file-directly

J'ai fini par y aller car après la mise à jour vers Python 3.7, import imp imprime:

DeprecationWarning: the imp module is deprecated in favour of importlib; see the module's documentation for alternative uses

et je ne sais pas comment désactiver cela.

Testé en Python 3.7.3.