Seite 1 von 1

Funktion ausführen, die als String vorliegt

Verfasst: Freitag 14. Januar 2011, 07:07
von gkuhl
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

Re: Funktion ausführen, die als String vorliegt

Verfasst: Freitag 14. Januar 2011, 08:06
von Darii
Inwiefern führen sie nicht zum Ziel? Wenn es damit schon nicht klappt klappt es nämlich in keinem Fall. Woran hapert es?

Re: Funktion ausführen, die als String vorliegt

Verfasst: Freitag 14. Januar 2011, 08:13
von Hyperion
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)

Re: Funktion ausführen, die als String vorliegt

Verfasst: Freitag 14. Januar 2011, 08:47
von 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.

Re: Funktion ausführen, die als String vorliegt

Verfasst: Freitag 14. Januar 2011, 08:51
von gkuhl
@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.

Re: Funktion ausführen, die als String vorliegt

Verfasst: Freitag 14. Januar 2011, 08:52
von Hyperion
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!

Re: Funktion ausführen, die als String vorliegt

Verfasst: Freitag 14. Januar 2011, 10:14
von gkuhl
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

Re: Funktion ausführen, die als String vorliegt

Verfasst: Freitag 14. Januar 2011, 10:30
von sma

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

Re: Funktion ausführen, die als String vorliegt

Verfasst: Freitag 14. Januar 2011, 12:45
von snafu
@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.

Re: Funktion ausführen, die als String vorliegt

Verfasst: Freitag 14. Januar 2011, 14:01
von sma
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

Re: Funktion ausführen, die als String vorliegt

Verfasst: Freitag 14. Januar 2011, 14:38
von Darii
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.

Re: Funktion ausführen, die als String vorliegt

Verfasst: Freitag 14. Januar 2011, 16:46
von gkuhl
@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