Créer un module Python à l'exécution

L'auteur de ce sujet a trouvé une solution à son problème.
Auteur du sujet

Salut,

Dans le cadre d'un mini-projet, je souhaite créer un module à partir de code Python sous forme de chaîne de caractère (str). Après plusieurs essais à partir de trucs foireux j'ai réussi à importer un module depuis un site. Cependant j'arrive que à exécuter les fonctions depuis le scope actuel, c'est à dire que si j'importe ce script (alea.py):

1
2
3
4
5
6
7
import string, random

def random_string(length):
    res = ''
    for _ in range(length):
        res += random.choice(string.ascii_lowercase)
    return res

… de cette manière:

1
2
3
4
5
tele_import('http://telecode.esy.es/alea.py')  # Je crée le module en RAM depuis un mini site créé pour l'occasion \o/

import alea

print(alea.random_string(16))

… Python va crasher et me donner ce traceback:

1
2
3
4
5
Traceback (most recent call last):
  File "C:/Users/*****/Desktop/Programmation/Python/TeleCode/main.py", line 13, in <module>
    print(alea.random_string(16))
  File "<string>", line 7, in random_string
NameError: name 'random' is not defined

J'en déduis que j'exécute la fonction dans le scope dans lequel je l'appelle et non dans le module dans lequel elle est contenue.

Voici comment je procède actuellement pour importer le module:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#!/usr/bin/env python3
# coding: utf-8

import sys
import os
from types import ModuleType, SimpleNamespace
import urllib.request


class TeleModule(ModuleType):
    def __new__(cls, name, doc, namespace, url):
        tm = super().__new__(cls, name, doc)
        namespace.__name__ = name
        namespace.__cached__ = name
        namespace.__file__ = url
        namespace.__doc__ = doc
        tm.namespace = namespace
        sys.modules[name] = tm
        return tm

    def __init__(self, name, doc, namespace, url):
        pass

    def __getattr__(self, item):
        return getattr(self.namespace, item)

    def __dir__(self):
        return dir(self.namespace)

    def __repr__(self):
        return '<module {!r} from {!r}>'.format(self.namespace.__name__, self.namespace.__file__)


def tele_import(url):
    exec(compile(urllib.request.urlopen(url).read().decode('utf-8'), '<string>', 'exec'))
    tm = TeleModule(os.path.basename(url).split('.')[0], '', SimpleNamespace(**locals()), url)
    return tm

Comme vous pouvez le voir c'est crade. J'aimerai donc savoir s'il existe un moyen plus propre et surtout qui marche pour créer des modules at runtime.

Merci d'avance,

AZ.

PS: Si quelqu'un saurait à quoi sert l'attribut __loader__, je n'ai pas compris la doc :(

Édité par felko

Anciennement AlphaZeta

+0 -0
Staff

Pour t'aider il faudrait comprendre le but réel de ta démarche. Ce qui t’intéresse c'est de charger un module depuis une chaine, importer un module depuis un fichier ou simplement executer du code python depuis une chaine ?

En bref, quel est le but final ?

Et est ce que le module à importer est dans le path python ?

edit: Ha est si tu veux vraiment faire ça a partir de string ou tu veux juste créé du code "à la volée". Pour ce dernier cas il est souvent possible de ne pas passer par une chaine

Édité par Kje

+0 -0
Auteur du sujet

Ce qui m'intéresse serait de charger un module "virtuel" qui ne serait chargé que sur la RAM, le code source du fichier est stocké en ligne. Le but est de pouvoir importer ce module virtuel depuis n'importe quelle machine dotée d'une connexion internet comme n'importe quel autre module. Sinon je souhaite vraiment faire ça à partir de string, parce que je ne connais pas les autres possibilités qui s'offrent à moi.

Et est ce que le module à importer est dans le path python ?

Tu te poses cette question à cause du import alea ? Si oui, je peux faire ça parce que j'ai ajouté à sys.modules mon module. Ça revient à redéfinir le path python en effet.

En fait, le truc que je recherche, c'est une fonction qui puisse à partir de code source Python me renvoyer le module correspondant à ces sources. Quelque chose du genre:

1
2
3
4
5
6
7
8
9
alea = code_source_vers_module_fonction_magique("""\
import random, string

def random_string(length):
    res = ''
    for _ in range(length):
        res += random.choice(string.ascii_lowercase)
    return res
""")

Édité par felko

Anciennement AlphaZeta

+0 -0
Staff

Cette réponse a aidé l'auteur du sujet

Dans ce cas là tu peux utiliser le fonction __import__ directement ou import_module dans importlib.

Sinon va sur cette page il y a l'exemple "Loading Modules from a Remote Machine Using Import Hooks" qui correspond peu ou prou a ce que tu veux faire avec les explications.

+1 -0
Vous devez être connecté pour pouvoir poster un message.
Connexion

Pas encore inscrit ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte