Closed VetriMaaran closed 3 years ago
VetriMaaran wrote at 2020-12-14 09:42 -0800:
from RestrictedPython import compile_restricted, safe_globals from RestrictedPython.PrintCollector import PrintCollector _print_ = PrintCollector _getattr_ = getattr source_code = """ print("Hello") print("Oh my god") print(dir()) result = printed """ byte_code = compile_restricted( source_code, filename='<string>', mode='exec', ) exec(byte_code, safe_globals) print(result)
This is my code but while I am using safe_globals it gives me an error.
name '_print_' is not defined
You must put your definitions of _print_
and _getattr_
into
safe_globals
: e.g. safe_globals["_print_"] = _print_
.
VetriMaaran wrote at 2020-12-14 09:42 -0800:
from RestrictedPython import compile_restricted, safe_globals from RestrictedPython.PrintCollector import PrintCollector _print_ = PrintCollector _getattr_ = getattr source_code = """ print("Hello") print("Oh my god") print(dir()) result = printed """ byte_code = compile_restricted( source_code, filename='<string>', mode='exec', ) exec(byte_code, safe_globals) print(result)
This is my code but while I am using safe_globals it gives me an error.name '_print_' is not defined
You must put your definitions of_print_
and_getattr_
intosafe_globals
: e.g.safe_globals["_print_"] = _print_
.
I did this but now I cannot see the printed statement on my console
I tried to print the result out now I get this error
NameError: name 'result' is not defined
How can I see the printed statement in my console?
VetriMaaran wrote at 2020-12-14 22:13 -0800:
...
You must put your definitions of
_print_
and_getattr_
intosafe_globals
: e.g.safe_globals["_print_"] = _print_
.I did this but now I cannot see the printed statement on my console
RestrictedPython
works as follows:
_getattr_
, print
is transformed into a
call to _print_
._getattr_
, _print_
) -- usually found
in safe_globals
.You have (indirectly) replaced Python's print
with your _print_
,
an instance of PrintCollector
. This does not print to the console
but collects the printed data, usually accessed (from restricted code)
via the name printed
.
If you want that print
prints to the console, you must provide
a different definition of _print_
. If you use Python 3 (or Python 2
with the "future" print_function
),
you could use safe_globals["_print_"] = print
.
@VetriMaaran I fixed your code, see below.
Some comments:
exec
does not see the variables in your module. (I created a copy of the safe_globals so it does not get modified.)dir
is not defined so I commented it out.exec
does not return anything so you have to put the return variable inside the globals dict and extract it from there afterwards.from RestrictedPython import compile_restricted, safe_globals
from RestrictedPython.PrintCollector import PrintCollector
source_code = """
print("Hello")
print("Oh my god")
# print(dir())
result = printed
"""
my_globals = safe_globals.copy()
my_globals['_print_'] = PrintCollector
my_globals['_getattr_'] = getattr
my_globals['result'] = None
byte_code = compile_restricted(
source_code,
filename='<string>',
mode='exec',
)
exec(byte_code, my_globals)
print(my_globals['result'])
VetriMaaran wrote at 2020-12-14 22:13 -0800: ... > You must put your definitions of
_print_
and_getattr_
intosafe_globals
: e.g.safe_globals["_print_"] = _print_
. I did this but now I cannot see the printed statement on my consoleRestrictedPython
works as follows: the Python source code is compiled into an "Abstract Syntax Tree" ("AST") the AST is transformed to make important operations externally controllable. In particular, attribute access is transformed into a call to_getattr_
,_print_
. * the application compiles the "AST" into byte code and executes this in an environment containing the necessary definitions (e.g._getattr_
,_print_
) -- usually found insafe_globals
. You have (indirectly) replaced Python's_print_
, an instance ofPrintCollector
. This does not print to the console but collects the printed data, usually accessed (from restricted code) via the nameprinted
. If you want that_print_
. If you use Python 3 (or Python 2 with the "future"print_function
), you could usesafe_globals["_print_"] = print
.
from RestrictedPython import compile_restricted, safe_globals
_print_ = print
safe_globals["_print_"] = _print_
source_code = """
print("Hello")
print("Oh my god")
result = printed
"""
byte_code = compile_restricted(
source_code,
filename='<string>',
mode='exec',
)
val = exec(byte_code, safe_globals)
This method does not seem to work
Error
val = exec(byte_code, safe_globals)
File "<string>", line 1, in <module>
TypeError: 'NoneType' object is not callable
@VetriMaaran I fixed your code, see below.
Some comments:
- You have to put the variables into the dict used as globals as
exec
does not see the variables in your module. (I created a copy of the safe_globals so it does not get modified.)dir
is not defined so I commented it out.exec
does not return anything so you have to put the return variable inside the globals dict and extract it from there afterwards.from RestrictedPython import compile_restricted, safe_globals from RestrictedPython.PrintCollector import PrintCollector source_code = """ print("Hello") print("Oh my god") # print(dir()) result = printed """ my_globals = safe_globals.copy() my_globals['_print_'] = PrintCollector my_globals['_getattr_'] = getattr my_globals['result'] = None byte_code = compile_restricted( source_code, filename='<string>', mode='exec', ) exec(byte_code, my_globals) print(my_globals['result'])
This works perfectly fine.
Printed contains only of global scope prints, but prints in functions are ignored
print = print safe_globals["print"] = print ... This method does not seem to work
Error
val = exec(byte_code, safe_globals) File "<string>", line 1, in <module> TypeError: 'NoneType' object is not callable
I have been wrong: you cannot directly use print
as definition for _print_
. What you define as _print_
must emulate RestrictedPython.PrintCollector.PrintCollector
.
What happens when you call print
in a context (either the module or a function context) is: as a preparatory step _print_
is called (in this context) with _getattr_
to define _print
; this happens once in each context (calling print
). The actual print calls are then transformed into _print._call_print(...)
. printed
is transformed into the call _print()
.
This description suggests how you can transform RestrictedPython
's PrintCollector
to provide your own custom print
implementation.
Printed contains only of global scope prints, but prints in functions are ignored
Is there a way to include all prints? Or specify which functions to include prints from? It looks like the PrintCollector constructor gets called multiple times, so I'm considering a static variable to combine them all manually.
bskinny129 wrote at 2024-5-7 20:25 -0700:
Printed contains only of global scope prints, but prints in functions are ignored
Is there a way to include all prints? Or specify which functions to include prints from? It looks like the PrintCollector constructor gets called multiple times, so I'm considering a static variable to combine them all manually.
A call with parameter _getattr_
to what you provide
as _print_
definition is injected at
the beginning of each restricted function which uses print
.
Your _print_
is expected to return an object _print with
methods __call__()
and _call_print(*objects, **kw)
.
The restricted compilation for the function has
translated references to printed
as _print()
and
print(...)
calls to _print._call_print(...)
calls.
That is what you can play with.
Especially, you can ensure a single (global) _print
(letting your _print_
always return the same object)
or a single _print
per (top level) function execution
(instantiating _print
once for such an execution and
passing lambda unused: _print
as _print_
).
Note that in both cases, surprises are possible if nested restricted
functions use printed
.
If the possibilities outlined above are not sufficient for you,
you can use a different RestrictedNodeTransformer
in a customized restricted_compilation
implementation.
Not sure I follow, but thanks for the detailed reply. I ended up making my own PrintCollector to store to a global variable (I'm running 10 Python files at a time)
`print_output = {0: [], 1: [], 2: [], 3: [], 4: [], 5: [], 6: [], 7: [], 8: [], 9: []}
class PrintCollector: """Collect written text, and return it when called."""
def __init__(self, _getattr_=None, index=None):
self.txt = []
self.index = index
#print("created: ", index)
self._getattr_ = _getattr_
def write(self, text):
#print("WRITE for ", self.index)
#print(text)
print_output[self.index].append(text)
self.txt.append(text)
def __call__(self):
#print("called: ", self.index)
#print("dict is: ", print_output[self.index])
#return ''.join(self.txt)
return ''.join(print_output[self.index])
def _call_print(self, *objects, **kwargs):
if kwargs.get('file', None) is None:
kwargs['file'] = self
else:
self._getattr_(kwargs['file'], 'write')
print(*objects, **kwargs)`
And
data = { "_print_": partial(PrintCollector, index=index), "__builtins__": { ...
Any surprises I should expect with this approach?
This is my code but while I am using safe_globals it gives me an error.