Question
La bibliothèque standard documente clairement comment importer directement les fichiers sources (étant donné le chemin d'accès absolu au fichier source), mais cette approche ne fonctionne pas si ce fichier source utilise des importations implicites de type sibling comme décrit dans l'exemple ci-dessous.
Comment cet exemple pourrait-il être adapté pour fonctionner en présence d'importations implicites de frères et sœurs ?
J'ai déjà vérifié ce et cet autre Questions Stackoverflow sur le sujet, mais elles ne traitent pas des importations implicites de frères et sœurs. sur le fichier étant importé à la main.
Configuration/Exemple
Voici un exemple illustratif
Structure du répertoire :
root/
- directory/
- app.py
- folder/
- implicit_sibling_import.py
- lib.py
app.py
:
import os
import importlib.util
# construct absolute paths
root = os.path.abspath(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))
isi_path = os.path.join(root, 'folder', 'implicit_sibling_import.py')
def path_import(absolute_path):
'''implementation taken from https://docs.python.org/3/library/importlib.html#importing-a-source-file-directly'''
spec = importlib.util.spec_from_file_location(absolute_path, absolute_path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
isi = path_import(isi_path)
print(isi.hello_wrapper())
lib.py
:
def hello():
return 'world'
implicit_sibling_import.py
:
import lib # this is the implicit sibling import. grabs root/folder/lib.py
def hello_wrapper():
return "ISI says: " + lib.hello()
#if __name__ == '__main__':
# print(hello_wrapper())
Running python folder/implicit_sibling_import.py
avec le if __name__ == '__main__':
bloc de rendement commenté ISI says: world
dans Python 3.6.
Mais courir python directory/app.py
rendements :
Traceback (most recent call last):
File "directory/app.py", line 10, in <module>
spec.loader.exec_module(module)
File "<frozen importlib._bootstrap_external>", line 678, in exec_module
File "<frozen importlib._bootstrap>", line 205, in _call_with_frames_removed
File "/Users/pedro/test/folder/implicit_sibling_import.py", line 1, in <module>
import lib
ModuleNotFoundError: No module named 'lib'
Solution de rechange
Si j'ajoute import sys; sys.path.insert(0, os.path.dirname(isi_path))
à app.py
, python app.py
donne world
comme prévu, mais j'aimerais éviter de broyer le sys.path
si possible.
Exigences de réponse
J'aimerais python app.py
pour imprimer ISI says: world
et j'aimerais y parvenir en modifiant le fichier path_import
fonction.
Je ne suis pas sûr des implications d'une mauvaise interprétation sys.path
. Par exemple, s'il y avait directory/requests.py
et j'ai ajouté le chemin vers directory
à la sys.path
je ne voudrais pas import requests
pour commencer à importer directory/requests.py
au lieu d'importer le bibliothèque des demandes que j'ai installé avec pip install requests
.
La solution MUST peut être implémenté sous la forme d'une fonction python qui accepte le chemin d'accès absolu au module souhaité et renvoie l'adresse du module. objet du module .
Idéalement, la solution ne devrait pas introduire d'effets secondaires (par exemple, si elle modifie le IIddeeaallllyy,, la ssoolluuttiioonn sshhoouulldd n'a pas I idinentatrlrolodydu,uc cete h sesi idsdeoe-l-eueftffifeoecnct tsss h (o(eueglg. d. inifof t i itit n dtdoroeoesds u mcmoeod disififydy e -effecItdse a(lelgy , itfh ei ts odlouetsi omno dsihfoyu ld n'introduit pas d'effets secondaires (par exemple, si elle modifie sys.path
il devrait retourner sys.path
à son état initial). Si la solution introduit des effets secondaires, il faut expliquer pourquoi il n'est pas possible de trouver une solution sans introduire d'effets secondaires.
PYTHONPATH
Si j'ai plusieurs projets qui font cela, je ne veux pas avoir à me rappeler de définir PYTHONPATH
à chaque fois que je passe de l'un à l'autre. L'utilisateur devrait juste être capable de pip install
mon projet et l'exécuter sans aucune configuration supplémentaire.
-m
Le site -m
drapeau est l'approche recommandée/pythonique, mais la bibliothèque standard documente aussi clairement les éléments suivants Comment importer directement les fichiers sources . J'aimerais savoir comment je peux adapter cette approche pour faire face aux importations relatives implicites. Il est clair que les internes de Python doivent le faire, alors comment les internes diffèrent-ils de la documentation "import direct des fichiers sources" ?
0 votes
En ce qui concerne Python, cet "import implicite de frère et soeur" est un import absolu ordinaire, et certainement pas un import relatif implicite. Les importations relatives implicites ne sont plus prises en charge dans Python 3.
1 votes
Modification du site
sys.path
est probablement votre meilleure option. Quoi que vous fassiez pour que la machinerie d'importation regarde dans le dossier de ce fichier, elle devra s'attarder au-delà de la durée de l'importation initiale, puisque les fonctions de ce fichier peuvent effectuer d'autres importations lorsque vous les appelez.0 votes
@user2357112 En effet PEP 8 indique que les importations implicites relatives sont désactivées dans Python 3. Mais je me demande : si l'exemple ci-dessus n'est pas une importation implicite relative, alors qu'est-ce qui l'est ? Avez-vous un exemple ?
0 votes
@user2357112 le
import lib
ne spécifie pas de nom de paquet et n'utilise pas non plus la fonction.
. La seule chose qui n'en fait pas une importation relative implicite est donc le fait qu'elle n'est pas effectuée dans un paquet ?0 votes
Cette question revient à demander : "Comment puis-je accomplir une chose sans utiliser les outils qui m'ont été donnés pour accomplir cette chose ?". Comme d'autres, je modifierais le sys.path.
1 votes
@DarrickHerwehe Comme indiqué dans la question, modifier
sys.path
est bien tant que vous pouvez le justifier. Dans ce cas, "une explication quant à la raison pour laquelle la manipulationsys.path
est la meilleure option" est tout ce qui est nécessaire.