Guten Morgen,
ich arbeite gerade einen einer Webanwendung für einen (hausinternen) Programmierwettbewerb. In dem Wettbewerb kann man eine Funktion (Python Code) über ein Formular einreichen und dann andere Funktionen zum Kampf herausfordern. Der eingereichte Python-Code liegt derzeit als String in einer sqlite3-Datenbank.
Das Programm, das diesen Kampf, abwickelt, soll jetzt die Funktion aus der Datenbank holen und bei Bedarf aufrufen. Mir fehlt jetzt ein Ansatz wie man, das am besten in Python umsetzen kann. "eval", "exec" und "compile" scheinen nicht zum Ziel zu führen. Ich hoffe jemand kann mir da weiterhelfen.
Grüße
Gerrit
Funktion ausführen, die als String vorliegt
- Hyperion
- Moderator
- Beiträge: 7478
- Registriert: Freitag 4. August 2006, 14:56
- Wohnort: Hamburg
- Kontaktdaten:
Ich fürchte fast, dass das nicht so einfach gehen wird! Wenn Du eval nicht nutzen magst (über die generelle Gefahr des ganzen Ansatzes bist Du Dir hoffentlich eh im klaren), wird es da wohl eng. Du müßtest letztlich die import Anweisung "verbiegen", so dass sie nicht auf ein FS zugreift, sondern auf die DB bzw. eben einen String daraus.
Wenn Du die Module im FS speichern könntest, wäre es eigentlich recht einfach möglich, da Du dann nur die Module importieren müßtest. Das kann man auch leicht dynamisch per String machen. Schau Dir mal den Code von import_string aus dem werkzeug-Modul an. (http://werkzeug.pocoo.org/docs/utils/#general-helpers)
Wenn Du die Module im FS speichern könntest, wäre es eigentlich recht einfach möglich, da Du dann nur die Module importieren müßtest. Das kann man auch leicht dynamisch per String machen. Schau Dir mal den Code von import_string aus dem werkzeug-Modul an. (http://werkzeug.pocoo.org/docs/utils/#general-helpers)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
assert encoding_kapiert
@Hyperion: So schwer ist es nun auch nicht, Module aus einer Datenbank zu laden. Über "sys.meta_path" und eigene "Finder"- und "Loader"-Klassen kann man den "import"-Mechanismus relativ leicht anpassen.
Seit Version 2.7 kann man übrigens auch einfach "importlib.import_module()" nutzen, um Module dynamisch zu laden.
Seit Version 2.7 kann man übrigens auch einfach "importlib.import_module()" nutzen, um Module dynamisch zu laden.
@Darii: "exec" funktioniert insofern, dass ich den Code wohl compilieren kann, er dann aber im Namespace herumliegt. Folgendes Beispiel:
Ich denke ich wäre schon mal zufrieden, wenn ich das Ergebnisse in seinen eigenen Namespace packen kann. Ich sehe da gerade aber keinen Weg.
@Hyperion: Die Idee es übers FS zu lösen, hatte ich auch schon. Allerdings gefällt mir der Ansatz nicht wirklich. Mir bereitet der Sicherheitsaspekt schon genug Kopfschmerzen.
Code: Alles auswählen
In [159]: code_string = """
def func(x):
return x+1
def main(n):
return [func(x) for x in range(n)]
"""
In[160]: exec code_string
In [167]: main(3)
Out[167]: [1, 2, 3]
@Hyperion: Die Idee es übers FS zu lösen, hatte ich auch schon. Allerdings gefällt mir der Ansatz nicht wirklich. Mir bereitet der Sicherheitsaspekt schon genug Kopfschmerzen.
- Hyperion
- Moderator
- Beiträge: 7478
- Registriert: Freitag 4. August 2006, 14:56
- Wohnort: Hamburg
- Kontaktdaten:
Interessant. Vielleicht habe ich vor solchen Sprachkonstrukten was immer zu viel Ehrfurcht?lunar hat geschrieben:@Hyperion: So schwer ist es nun auch nicht, Module aus einer Datenbank zu laden. Über "sys.meta_path" und eigene "Finder"- und "Loader"-Klassen kann man den "import"-Mechanismus relativ leicht anpassen.
![Very Happy :-D](./images/smilies/icon_biggrin.gif)
Interessant, danke für den Hinweis!lunar hat geschrieben: Seit Version 2.7 kann man übrigens auch einfach "importlib.import_module()" nutzen, um Module dynamisch zu laden.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
assert encoding_kapiert
Danke für den Tipp. Habe mir jetzt mal folgendes zusammengebastelt:lunar hat geschrieben:Über "sys.meta_path" und eigene "Finder"- und "Loader"-Klassen kann man den "import"-Mechanismus relativ leicht anpassen.
Code: Alles auswählen
import imp
import sqlite3
class SQLite3Import(object):
def __init__(self, filename, sql):
self._filename = filename
self._sql = sql
def find_module(self, fullname, path=None):
identity = fullname.split('_')[-1]
conn = sqlite3.connect(self._filename)
c = conn.cursor()
c.execute(self._sql, (identity,))
result = c.fetchone()
c.close()
if result is not None:
self._code = result[0]
return self
def load_module(self, fullname):
code = self._code
mod = sys.modules.setdefault(fullname, imp.new_module(fullname))
mod.__file__ = "<%s>" % self.__class__.__name__
mod.__loader__ = self
exec code in mod.__dict__
return mod
if __name__ == "__main__":
import sys
finder = SQLite3Import("test.db", "SELECT code FROM test WHERE id=?")
sys.meta_path.append(finder)
import test_1
print test_1.main(12)
Gerrit
Code: Alles auswählen
from types import ModuleType
def compileInModule(n, s):
module = ModuleType(n)
exec s in module.__dict__
return module
demo = compileInModule("demo", "def f(x): return x + 1")
print demo.f(1)
Stefan
Zuletzt geändert von sma am Freitag 14. Januar 2011, 14:02, insgesamt 1-mal geändert.
Da spricht gar nichts gegen. Ich wusste halt nicht auswendig den Namen, also nahm ich den Typ des ersten besten Moduls, das mir einfiel und dachte dann, na, zieh das mal als Variable raus. Ich ändere das mal.
Stefan
Stefan
Dann frag das beim nächsten Mal auch. Macht alles einfacher und niemand muss raten, dass du exec ... in ... suchst.gkuhl hat geschrieben:@Darii: "exec" funktioniert insofern, dass ich den Code wohl compilieren kann, er dann aber im Namespace herumliegt. Folgendes Beispiel:
Ich denke ich wäre schon mal zufrieden, wenn ich das Ergebnisse in seinen eigenen Namespace packen kann. Ich sehe da gerade aber keinen Weg.
@Darii: Ich hatte in dem Moment einiges mit "exec" ausprobiert und hatte irgendwie das Gefühl in einer Sackgasse zu stecken. Ich schiebe das mal auf die (für mich) späte Stunde kurz vor Feierabend. Ich prangere hier eigentlich auch immer an, Fragen richtig zu stellen...
@sma: Dein Ansatz gefällt mir. Werde ich wohl übernehmen.
Grüße
Gerrit
@sma: Dein Ansatz gefällt mir. Werde ich wohl übernehmen.
Grüße
Gerrit