2 votes

Importer des modules qui n'existent pas (encore)

     Je souhaite créer ma propre variation de amoffat'ssh où il peut importer à peu près n'importe quelle commande du chemin UNIX de l'utilisateur, comme :

from sh import hg

     Cependant, j'ai du mal à trouver un moyen d'intercepter / de remplacer la méthode de python import [...] y from [...] import [...] . Pour l'instant, j'ai simplement besoin d'un moyen d'obtenir au moins [le nom de] l'objet de l'opération. from à ce moment-là, je peux simplement setattr() y partial() mon chemin à partir de là, j'espère. Je ne sais pas du tout comment faire pour l'instant, et je n'ai donc aucun code à présenter.

     L'essentiel de ce que je veux :

from test import t # Even though "t" doesn't exist in the module (yet)

     Toute aide concernant le code complet serait grandement appréciée !


Réponse finale, consolidée :

def __getattr__(name):
    if name == '__path__': raise AttributeError
    print(name)

4voto

L3viathan Points 706

Il y a en fait un moyen direct si vous utilisez Python 3.7+. , PEP-562 qui vous permet de définir __getattr__ au niveau du module :

def __getattr__(name):
    if name == "t":
        return "magic"
    raise AttributeError(f"module {__name__!r} has no attribute {name!r}")

Il existe également une fonction __dir__ que vous pouvez définir pour déclarer ce que le module intégré dir() sera dit à propos des noms dans votre module.


Quoi sh est plus sophistiquée, puisqu'elle veut prendre en charge les versions inférieures à 3.7 : Modification de l'environnement de travail de l'entreprise sys.modules et remplacer le module par un objet spécial qui se fait passer pour un module.

1voto

mfripp Points 46

Comme l'a fait remarquer @L3viathan, c'est facile à partir de Python 3.7 : il suffit de définir un __getattr__ dans votre module spécial. Ainsi, par exemple, vous pouvez créer un module "echo" (qui renvoie simplement le nom de l'objet demandé) comme ceci :

echo.py (Python >=3.7)

def __getattr__(name):
    return name

Alors vous pourriez l'utiliser comme ceci :

from echo import x
print(repr(x))
# 'x'

Sur les versions antérieures de Python, vous devez sous-classer le module, comme le suggère la rubrique PEP-562 . Cela fonctionne également dans Python 3.7.

echo.py (Python >=2)

import sys, types

class EchoModule(types.ModuleType):
    def __getattr__(self, name):
        return name

sys.modules[__name__] = EchoModule(__name__)

Vous l'utiliserez de la même manière que la version 3.7 : from echo import something .

Mise à jour

Pour une raison quelconque, Python tente de récupérer l'attribut deux fois pour chaque from echo import <x> appel. Il appelle également __getattr__('__path__') lorsque le module est chargé. Vous pouvez éviter les effets secondaires dans ces cas avec le code suivant :

echo.py (ne définir les attributs qu'une seule fois)

import sys, types

class EchoModule(types.ModuleType):
    def __getattr__(self, name):
        # don't define __path__ attribute
        if name == '__path__':
            raise AttributeError
        print("importing {}".format(name))
        # create the attribute in case it's required again
        setattr(self, name, name)
        # return the new attribute
        return getattr(self, name)

sys.modules[__name__] = EchoModule(__name__)

Ce code crée un attribut dans le fichier echo à chaque fois qu'un attribut précédemment inutilisé est importé (un peu comme le module collections.defaultdict ). Ensuite, si Python tente d'importer à nouveau ce même attribut par la suite, il le tirera directement du module au lieu de faire appel à __getattr__ (c'est comportement normal pour les attributs des objets).

Il y a aussi un peu de code ici pour éviter de mettre en place un mauvais __path__ ce qui permet également d'éviter d'exécuter votre code lorsque l'attribut __path__ est demandé. Notez que c'est peut-être la partie la plus importante ; lorsque j'ai testé, le simple fait d'élever la valeur de AttributeError para __path__ était suffisant pour empêcher le double accès à l'attribut nommé.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X