You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
QuestionFunctional allows the user to have a Python function that can generate an answer in response to scenario and agent attributes. However, this makes it not safely serializable. This example shows a way we could do it using RestrictedPython.
When instantiated, we grab the source code for the function
When serialized, the source code and function name are stored as strings
When deserialized, the source code and function are added to the object but not activated unless user calls activate function that uses the RestrcitedPython version
We have an "activate_dangerously" function that allows any function.
@zer0dss can you review and give your thoughts on safety?
importinspectimporttextwrapfromRestrictedPythonimportcompile_restricted, safe_globals, limited_builtinsfromRestrictedPython.Evalimportdefault_guarded_getitemfromRestrictedPython.Guardsimportsafe_builtins, full_write_guard, guarded_iter_unpack_sequenceclassQuestionFunctionlRunningException(Exception):
passclassQuestionFunctionlActivatedException(Exception):
passclassDummyQuestion:
def__init__(self, func: None):
iffuncisnotNone:
self._func=funcself.activated=Trueself.source_code=inspect.getsource(func)
self.function_name=func.__name__else:
self._func=Noneself.activated=Falseself.source_code=Noneself.function_name=Nonedeffunc(self, *args, **kwargs):
ifself.activated:
try:
returnself._func(*args, **kwargs)
exceptExceptionase:
# This is likely to be a RestrictedPython errorprint("An error occurred:", e)
raiseQuestionFunctionlRunningException(
textwrap.dedent("""\ The reason you received an exception is that the function might not safe to run. edsl uses RestrictedPython <https://pypi.org/project/RestrictedPython/>. If you you are ***sure*** it is OK to run, you can activate the function with the `activate_dangerously` method >>> q.activate_dangerously() """))
else:
raiseQuestionFunctionlActivatedException("Function not activated. Please activate it first with `activate` method.")
defto_dict(self):
return {"function_source_code": self.source_code, 'function_name': self.function_name}
defactivate(self) ->None:
"""Activate the function using RestrictedPython."""safe_env=safe_globals.copy()
safe_env['__builtins__'] = {**safe_builtins, 'getattr': getattr}
byte_code=compile_restricted(self.source_code, '<string>', 'exec')
loc= {}
try:
exec(byte_code, safe_env, loc)
self._func=loc[self.function_name]
self.activated=TrueexceptExceptionase:
print("An error occurred:", e)
defactivate_dangerously(self) ->None:
"""Activate the function without checking the safety."""exec(self.source_code, globals(), locals())
self._func=locals()[self.function_name]
self.activated=True@classmethoddeffrom_dict(cls, data:dict, activate:bool=False):
"""Create a `QuestionFunctional` from a dictionary."""new_q=cls(None)
# Not allowing loops for now#safe_env['_getiter_'] = iter#safe_env['_iter_unpack_sequence_'] = guarded_iter_unpack_sequence#safe_env['_write_'] = full_write_guard # Optional, for variable assignments within the loop#safe_env['_inplacevar_'] = _inplacevar_ # Handle augmented assignmentsnew_q.source_code=data["function_source_code"]
new_q.function_name=data["function_name"]
new_q.activated=Falseifactivate:
new_q.activate()
returnnew_qdefuser_function(x):
y=0foriinrange(100):
y+=xreturnyq1=DummyQuestion(user_function)
q2=DummyQuestion.from_dict(q1.to_dict())
try:
print(q2.func(12))
exceptQuestionFunctionlActivatedExceptionase:
print("Doesn't work because the function is not activated yet.")
try:
q2.activate()
print(q2.func(12))
exceptQuestionFunctionlRunningExceptionase:
print("Doesn't work because the function has a loop.")
q2.activate_dangerously()
print(q2.func(12))
The text was updated successfully, but these errors were encountered:
@johnjosephhorton using 'getattr': getattr is not safe in extreme cases. By default inside safe_builtins getattr is defined as safer_getattr and we can use that.
Using safe_env['__builtins__'] = {**safe_builtins} is safer.
I see - want to re-factor this code to do that? We might also consider re-factor "activate()" to allow for different levels of security. E.g., one that allows loops, for example.
QuestionFunctional
allows the user to have a Python function that can generate an answer in response to scenario and agent attributes. However, this makes it not safely serializable. This example shows a way we could do it using RestrictedPython.@zer0dss can you review and give your thoughts on safety?
The text was updated successfully, but these errors were encountered: