Funktion ausführen, die als String vorliegt

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Benutzeravatar
gkuhl
User
Beiträge: 600
Registriert: Dienstag 25. November 2008, 18:03
Wohnort: Hong Kong

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
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

Inwiefern führen sie nicht zum Ziel? Wenn es damit schon nicht klappt klappt es nämlich in keinem Fall. Woran hapert es?
Benutzeravatar
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)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
lunar

@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.
Benutzeravatar
gkuhl
User
Beiträge: 600
Registriert: Dienstag 25. November 2008, 18:03
Wohnort: Hong Kong

@Darii: "exec" funktioniert insofern, dass ich den Code wohl compilieren kann, er dann aber im Namespace herumliegt. Folgendes Beispiel:

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]
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.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

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.
Interessant. Vielleicht habe ich vor solchen Sprachkonstrukten was immer zu viel Ehrfurcht? :-D
lunar hat geschrieben: Seit Version 2.7 kann man übrigens auch einfach "importlib.import_module()" nutzen, um Module dynamisch zu laden.
Interessant, danke für den Hinweis!
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Benutzeravatar
gkuhl
User
Beiträge: 600
Registriert: Dienstag 25. November 2008, 18:03
Wohnort: Hong Kong

lunar hat geschrieben:Über "sys.meta_path" und eigene "Finder"- und "Loader"-Klassen kann man den "import"-Mechanismus relativ leicht anpassen.
Danke für den Tipp. Habe mir jetzt mal folgendes zusammengebastelt:

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)
Grüße
Gerrit
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

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)
Erzeuge selbst ein neues Modul. Kompiliere dann das Programm im Kontext dieses Moduls, gibt das Modul zurück, damit es (wie bei Import) an eine Variable gebunden werden kann. Rufe dann die Funktion auf.

Stefan
Zuletzt geändert von sma am Freitag 14. Januar 2011, 14:02, insgesamt 1-mal geändert.
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

@sma: Was spricht gegen `types.ModuleType`? In seiner jetzigen Form wäre der Code z.B. unter Jython nicht lauffähig, da er dort `__builtins__` nicht finden kann.
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

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
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

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.
Dann frag das beim nächsten Mal auch. Macht alles einfacher und niemand muss raten, dass du exec ... in ... suchst.
Benutzeravatar
gkuhl
User
Beiträge: 600
Registriert: Dienstag 25. November 2008, 18:03
Wohnort: Hong Kong

@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
Antworten