Bienvenue sur IndexError.

Ici vous pouvez poser des questions sur Python et le Framework Django.

Mais aussi sur les technos front comme React, Angular, Typescript et Javascript en général.

Consultez la FAQ pour améliorer vos chances d'avoir des réponses à vos questions.

toc toc, qui demande l'import?

+3 votes

Est-il possible de connaitre le nom du script faisant l’import d’un module au sein de ce même module?

En somme j’aimerais qu’un module puisse avoir un comportement différent si c’est script1.py qui veut l’importer ou si c’est script2.py

demandé 17-Jun-2015 par anonyme

Pas très bien pigé ce que tu cherches =/

Peux-tu préciser à quoi va servir cette utilisation ?

J'en sais rien de ce que tu essayes de realiser, mais il est clair que cette maniere de faire, n'est pas la bonne :\

3 Réponses

0 votes

pourquoi ne pas mettre un décorateur différent dans tes scripts 1 et 2, sur les fonctions que tu importes ?

répondu 17-Jun-2015 par buffalo974 (2,632 points)

Avec un exemple c'est toujours plus clair x)

+4 votes

Çà a l'air jouable.

Des éléments de réponse sur SO. Ici il override la fonction import et joue avec le module inspect.

Quelque chose du genre

import inspect
caller = inspect.currentframe().f_back
caller_name = caller.f_globals.get('__name__')
if caller_name == "something":
    do_something()
else:
    do_somethingelse()

Bon après comme un module n'est chargé qu'une seule fois, çà ne marchera pas si tu compte faire ça au sein du même programme :

(sandbox)~/s/s/ie ❯❯❯ cat a.py
import inspect
print('loaded')
caller = inspect.currentframe().f_back
print( caller.f_globals.get('__name__') )

(sandbox)~/s/s/ie ❯❯❯ cat b.py
import a

(sandbox)~/s/s/ie ❯❯❯ cat c.py
import a

(sandbox)~/s/s/ie ❯❯❯ cat d.py 
# b et d importent tous les deux le module a
import b
import c
(sandbox)~/s/s/ie ❯❯❯ python d.py
loaded
b
# le print ne s’exécute qu'une seule foi

Après, je ne sais pas dans quel contexte tu souhaites faire ça, mais ça ne me parait pas être une bonne idée a la base ;)

répondu 17-Jun-2015 par jc (2,674 points)

Merci pour la/les réponses! =)

De fait je conçois que ça paraisse être une drôle d'idée mais qui peut-être (enfin je le crois) utile dans certains cas.
Le mien étant le suivant:
j'ai coder un joli petit module pour soumettre et gérer des jobs sur un/des cluster(s). Plusieurs scripts ont besoins de ce module et à tous ces scripts sont affecter un logging bien précis.
Donc si je peux récupérer le nom du script effectuant l'import, je peux alors assigner le bon logger lors de l'import du module =)

ca me parait ultra bourrin :)

J'ai bien compris ta problématique, mais ce qui reste surprenant c'est que ton module se comporte différement suivant qui l'appelle : du coup il doit avoir conscience des autres modules.

L'idée c'est plutot de faire l'inverse : ton module se comporte de la meme maniere peut importe qui l'appelle, et tu passe un parametre. Le comportement change sur le comment le code est appelé.

Pourquoi ne pas prendre le probleme dans l'autre sens : les scripts principaux configureraient le logging, avant d'appeler les fonctions de ton module.
Ca peut peut etre se régler avec logging.basicConfig

Ou avoir une fonction qui configure l'innstance du logger de ton module en lui donnant les bons parametres ?

Je suis a 400% d'accord avec @jc, tu devrais reformuler la question, mieux decrire le probleme.

+8 votes

C'est pas une bonne idée.

Donc, puisqu'il existe une réponse pour le faire quand même (cf réponse de jc), voilà une alternative, qui, à défaut d'être la meilleure, est toujours plus élégante, je pense, que ce que demande l'OP.

Spécifications

Un module doit avoir un comportement différent selon celui qui l'importe est une idée qui sent mauvais dés qu'on se rend compte que cela est presque équivalent à un module ne doit pas être déterministe.

Comment on fait un truc plus propre ?
Un module avec des comportements différents selon des options semble déjà plus propre, et en plus est facilement extensible.

Et ça, c'est du pattern strategy.

Bon, dis comme ça, c'est pas évident à mettre en place. Il y a deux cas, selon ce que le module contient.

des fonctions

Ya pas à baver, il faut un argument en plus un peu partout, ou faire du python.
La première est triviale ET ennuyeuse (modif des fonctions, toussa)
La seconde est vachement fun, parce que ya des décorateurs ou du currying.

Dans un premier temps, voilà le main :

import michel

michel.init_package(mode=1)
print(michel.forever(' tonigh'))
print(michel.tonigth('!'))

# pareil mais avec le mode 2 enclenché :
# michel.init_package(mode=2)
# print(michel.forever(' tonigh'))
# print(michel.tonigth('!'))

et vient ensuite le module michel, implémenté dans le sous répertoire michel et donc accessible par le chemin michel/michel.py:

def forever(truc):
    return 'forever' + truc

def tonigth(bidule):
    return bidule * 2

On peut ajouter quelque chose comme ça dans l'init.py :

from michel import michel

def mode1(func, *args, **kwargs):
    def wrapped(*args, **kwargs):
        return func(*args, **kwargs) + ' hadoken'
    return wrapped

def mode2(func, *args, **kwargs):
    def wrapped(*args, **kwargs):
        return func('obélix')
    return wrapped

def init_package(mode):
    assert(mode in (1,2)) # script 1 ou script 2 ou la mort
    for name,func in ((k,v) for k,v in michel.__dict__.items() if callable(v)):
        if mode == 1:
            globals()[name] = mode1(func)
        if mode == 2:
            globals()[name] = mode2(func)

Et voilà ! À chacun de détailler ce que font effectivement les décorateurs (modif des arguments, de la valeur de retour).
Le cours de sametmax est assez complet là dessus, et propose des techniques encore plus sioux.

des objets

Pareil, mais avec le vrai pattern strategy. Une classe aussi abstraite que possible (python oblige), et des instances de MichelStrategy, qui font ce qu'on leur demande.

Basiquement :

class MichelStrategy:
    def forever(truc): return 'forever' + truc
    def tonigth(bidule): return bidule * 2

class MichelStrategyMode1(MichelStrategy):
    def forever(truc): return super().forever(truc) + ' hadoken'
    def tonigth(bidule): return super().tonigth(bidule) + ' hadoken'

class MichelStrategyMode2(MichelStrategy):
    def forever(truc): return super().forever('obélix')
    def tonigth(bidule): return super().tonigth('obélix')
répondu 18-Jun-2015 par lucas (2,292 points)
edité 18-Jun-2015 par lucas

Solution vraiment élégante qui mérite de l'upvote :)

...