2010-12-29 5 views
4

Betrachten Sie das folgende:Python eval (Kompilieren (...), Sandbox), Globals gehen in Sandbox, außer in def, warum?

def test(s): 
    globals()['a'] = s 

sandbox = {'test': test} 
py_str = 'test("Setting A")\nglobals()["b"] = "Setting B"' 
eval(compile(py_str, '<string>', 'exec'), sandbox) 

'a' in sandbox # returns False, !What I dont want! 
'b' in sandbox # returns True, What I want 
'a' in globals() # returns True, !What I dont want! 
'b' in globals() # returns False, What I want 

ich nicht einmal sicher bin, wie zu fragen, aber ich möchte den globalen Rahmen für eine Funktion, die Umwelt ich es in die Absicht zu sein, zu laufen, ohne die Funktion während der kompilieren zu müssen Eval. Ist das möglich?

Vielen Dank für jede Eingabe

Lösung

def test(s): 
    globals()['a'] = s 

sandbox = {} 

# create a new version of test() that uses the sandbox for its globals 
newtest = type(test)(test.func_code, sandbox, test.func_name, test.func_defaults, 
        test.func_closure) 

# add the sandboxed version of test() to the sandbox 
sandbox["test"] = newtest 

py_str = 'test("Setting A")\nglobals()["b"] = "Setting B"' 
eval(compile(py_str, '<string>', 'exec'), sandbox) 

'a' in sandbox # returns True 
'b' in sandbox # returns True 
'a' in globals() # returns False 
'b' in globals() # returns False 
+0

Was wollen Sie tun, und in welcher Weise sie sich von einer Klasse und eine Methode? Machen Sie eine Methode einer Klasse "test" und verwenden Sie den Namespace 'self'. Inwiefern funktioniert das nicht für dich? –

+0

Das Einzige, was ich tun möchte, ist tiefer in die inneren Abläufe von Python einzutauchen, aus meinem persönlichen Interesse, die Sprache in der Tiefe zu erforschen. Wie die meisten Dinge ergibt sich diese Frage aus einem Problem der realen Welt, aber eines, das ich bereits gelöst habe. Das ist einfach das Streben nach Wissen. –

Antwort

6

Wenn Sie eine Funktion in Python nennen, die globalen Variablen es sieht immer die Globals des Moduls es in definiert wurde (Wenn dies nicht der Fall war, kann die Funktion nicht funktionieren - es könnte in der Tat brauchen einige globale Werte, und Sie wissen nicht unbedingt, welche das sind.) Angeben eines Globalen Wörterbuch mit exec oder eval() betrifft nur die Globals, die der Code exec 'd oder eval()' d ist.

Wenn eine Funktion andere Globals sehen soll, müssen Sie die Funktionsdefinition tatsächlich in die Zeichenfolge aufnehmen, die Sie an exec oder eval() übergeben. Wenn Sie das tun, ist das "Modul" der Funktion die Zeichenfolge, aus der es kompiliert wurde, mit seinen eigenen Globalen (d. H. Den von Ihnen gelieferten).

Sie könnten dies umgehen, indem Sie eine neue Funktion mit dem gleichen Code-Objekt wie das, das Sie anrufen, aber ein anderes func_globals Attribut, das auf Ihre globalen dict verweist, aber das ist ziemlich fortgeschritten Hacker und wahrscheinlich nicht wert. Dennoch ist hier, wie Sie es tun würde:

# create a sandbox globals dict 
sandbox = {} 

# create a new version of test() that uses the sandbox for its globals 
newtest = type(test)(test.func_code, sandbox, test.func_name, test.func_defaults, 
        test.func_closure) 

# add the sandboxed version of test() to the sandbox 
sandbox["test"] = newtest 
+0

Danke, das ist genug, um mich in die richtige Richtung für mehr Forschung zu zeigen, in der ich diesen Link gefunden habe. http://code.activestate.com/recipes/577283-decorator-to-expose-local-variables-of-a-function-/ Wie aus dem Link "Ich habe versucht versucht, f.func_globals mit einem Benutzerwörterbuch [...] das funktioniert nicht, da der Python-Interpreter das func_globals-Wörterbuch nicht mit Python-Aufrufen aufruft, sondern direkt mit PyDict_GetItem " Ich bin müde und nicht vollständig verständlich, aber es sieht so aus, als wäre es mit reinem Python nicht möglich Hackerei. Ich werde aber Spaß mit dem Byteplay haben. Nochmals vielen Dank –

+0

Hinzugefügt Beispiel zum Erstellen einer neuen Version der Funktion, die die Sandbox sieht. – kindall

+0

sehr cool, ich wusste nicht, ein Typ Objekt war so aufrufbar, die Python-Docs nicht besonders Hinweis auf so etwas wie es Aufmerksamkeit, mindestens meine, um es als Konstruktor für Klassen zu verwenden. –

2

Externe Ausführungskontexte statisch in Python definiert sind (f.func_globals schreibgeschützt ist), würde ich so sagen, dass Sie wollen, was nicht möglich ist. Der Grund dafür ist, dass die Funktion ungültig werden könnte, wenn Python seinen Definitionskontext zur Laufzeit ändert. Wenn die Sprache es erlaubte, wäre dies ein extrem einfacher Weg für die Einschleusung von bösartigem Code in Bibliotheksaufrufe. .

def mycheck(s): 
    return True 

exec priviledged_code in {'check_password':mycheck} 
2

Sandbox-Code für exec durch alternative Globals/Einheimische bieten hat viele Einschränkungen:

  • Die alternativen Globals/Einheimischen nur für den Code gelten Der Sandkasten. Sie beeinflussen nichts außerhalb davon, sie können nichts außerhalb davon beeinflussen, und es würde keinen Sinn ergeben, wenn sie es könnten.

    Um es anders auszudrücken, Ihre so genannte "Sandbox" übergibt das Objekt test an den Code von exec. Um die Globals zu ändern, die test sieht, müsste auch das Objekt geändert werden, nicht übergeben wie es ist. Das ist in keiner Weise möglich, damit es funktioniert, geschweige denn, dass das Objekt weiterhin etwas Sinnvolles tun würde.

  • Mit den alternativen Globals würde alles in der sandbox immer noch die Builtins sehen. Wenn Sie einige oder alle Butintins aus dem Code in der Sandbox ausblenden möchten, müssen Sie einen Schlüssel zu Ihrem Wörterbuch hinzufügen, das entweder auf None (deaktiviert alle Builtins) oder auf Ihre Version von ihnen zeigt. Dies schränkt auch bestimmte Attribute der Objekte ein, beispielsweise wird der Zugriff auf das Attribut func_globals einer Funktion deaktiviert.

  • Auch wenn Sie die Builtins entfernen, wird die Sandbox immer noch nicht sicher sein. Sandbox Code, dem du zuerst vertraust.

Hier ist ein einfaches Proof of Concept:

import subprocess 
code = """[x for x in().__class__.__bases__[0].__subclasses__() 
      if x.__name__ == 'Popen'][0](['ls', '-la']).wait()""" 
# The following runs "ls"... 
exec code in dict(__builtins__=None) 
# ...even though the following raises 
exec "(lambda:None).func_globals" in dict(__builtins__=None) 
+1

Während dies informativ ist, befasst es sich nicht mit dem vorliegenden Thema. Ich brauchte keine Lektion in Bezug auf die Vorbehalte/Unsicherheiten von Eval. Es gibt bereits viele andere Fragen zu Stackoverflow, die dieses Thema abdecken. –