How to run ipython script in python? - python

I'd like to run ipython script in python, ie:
code='''a=1
b=a+1
b
c'''
from Ipython import executor
for l in code.split("\n"):
print(executor(l))
that whould print
None
None
2
NameError: name 'c' is not defined
does it exists ? I searched the doc, but it does not seems to be (well) documented.

In short, depending on what you want to do and how much IPython features you want to include, you will need to do more.
First thing you need to know is that IPython separates its code into blocks.
Each block has its own result.
If you use blocks use this advice
If you don't any magic IPython provides you with and don't want any results given by each block, then you could just try to use exec(compile(script, "exec"), {}, {}).
If you want more than that, you will need to actually spawn an InteractiveShell-instance as features like %magic and %%magic will need a working InteractiveShell.
In one of my projects I have this function to execute code in an InteractiveShell-instance:
https://github.com/Irrational-Encoding-Wizardry/yuuno/blob/master/yuuno_ipython/ipython/utils.py#L28
If you want to just get the result of each expression,
then you should parse the code using the ast-Module and add code to return each result.
You will see this in the function linked above from line 34 onwards.
Here is the relevant except:
if isinstance(expr_ast.body[-1], ast.Expr):
last_expr = expr_ast.body[-1]
assign = ast.Assign( # _yuuno_exec_last_ = <LAST_EXPR>
targets=[ast.Name(
id=RESULT_VAR,
ctx=ast.Store()
)],
value=last_expr.value
)
expr_ast.body[-1] = assign
else:
assign = ast.Assign( # _yuuno_exec_last_ = None
targets=[ast.Name(
id=RESULT_VAR,
ctx=ast.Store(),
)],
value=ast.NameConstant(
value=None
)
)
expr_ast.body.append(assign)
ast.fix_missing_locations(expr_ast)
Instead doing this for every statement in the body instead of the last one and replacing it with some "printResult"-transformation will do the same for you.

Related

Python Jupyter/Notebook: How to display a variable as text on a cell without copy/paste

It happens to me that when reading/reviewing the code, I becomes easier if I can see the 'look' of the variable a function is processing.
For that, I'd like to display a 'static' version of an instance of that variable (as a visual aid).
That variable may not be there on another run of the notebook, that's why it has to be text, not output.
This is also useful when creating documentation within the notebook.
With this little function
#----------------------------------
def vdisplay(var):
"""Converts the var to a pretty string and inserts
it on a new cell just below the present one.
Then you have to change that 'next cell' type to Markdown and execute it.
"""
# To print the var nicely.
from pprint import pformat as pf
string_to_insert=f"""
This is how it looks like:
```
{pf(var)}
```
"""
# Create a code cell and insert a string in it
get_ipython().set_next_input(string_to_insert)
return
#----------------------------------
You can do this
# This is the data we want to show
x={i:str(i)*i for i in range(10)}
# Show it!
vdisplay(x)
Visually:
I use the mouse intentionally so you can see the steps. Using keyboard shortcuts is much quicker.
Story: I explored several venues. The last one was a combination of
%store var f.txt and %load f.txt but that involved some manual
work. The evolution of that method is the one above.

Bokeh panel not printed when is inside an if-statement

I am using Bokeh to print out a multiple sequence alignment. So far, it has worked perfectly. For example:
[*Previous code that was not included*]
aln = AlignIO.read(alignment_file,'fasta')
p = view_alignment(aln, plot_width=900)
pn.pane.Bokeh(p)
However, I am trying to introduce an if-statement as it follows, it simply does not print anything:
if result == 1:
print('The gen you selected does not have any ortholog.')
else:
aln = AlignIO.read(alignment_file,'fasta')
p = view_alignment(aln, plot_width=900)
pn.pane.Bokeh(p)
I have introduced a print('Check if it works') inside the else part of the if-statement and it does print it. But the bokeh panel doesn't get printed.
I am using JupyterLab to try these things version 3.2.1. Not entirely sure what's happening. The reason why I am including this if-statement is to filter the gen that do not have any orthologs. Otherwise, it would print simply one sequence and it does not make sense for what I am trying to achieve here.
Any suggestion?
I found the solution to my problem.
I imported show from bokeh.io.
from bokeh.io import show
Finally simply call the graph that it was already form as:
show(p)
Everything is working as it should now!

Function properly defined and working but not printing results in terminal

I recently wrote a simple function that gets the present value of an asset, the function PV works properly and I have tested it. The function prints well in my pv file. However, when I run this code in the main file it does not print the output to the terminal and just closes after taking the inputs. Is there a reason for this? For reference the functions just perform some simple mathematics problems.
Below is a minimal reproduction, both files are in the same folder.
in file_x you have a function that works like this:
def func(w,x,y,z):
z1 = w/(1 + (x/100))**(y*z)
print(z1)
return 0
this function is then imported into another file which is written like so
from file_x import func
w = int(input("Future Value: "))
x = float(input("ROR: "))
y = float(input("# of periods: "))
z = float(input("# of payment per anum: "))
func(w,x,y,z)
My problem is that when I run the 2nd file it takes the inputs properly but does not print the result from the function. Hope it was explained properly
In interactive mode an unused expression (here PV(w,x,y,z) which does returns a value and that value is not used) is printed to the terminal, because it is a common way to just display expression values.
But when you run a script in a non interactive mode (python script.py) then those unused expression are just discarded. As a general rule, you should never have such an unused expression in a script, because the behaviour will depend on the interactive mode of the interpretor.
So you should be explicit:
_ = PV(w,x,y,z) # value will be discarded even in interactive mode
or
print(PV(w,x,y,z)) # value will be printed even in non interactive mode

How can i accept and run user's code securely on my web app?

I am working on a django based web app that takes python file as input which contains some function, then in backend i have some lists that are passed as parameters through the user's function,which will generate a single value output.The result generated will be used for some further computation.
Here is how the function inside the user's file look like :
def somefunctionname(list):
''' some computation performed on list'''
return float value
At present the approach that i am using is taking user's file as normal file input. Then in my views.py i am executing the file as module and passing the parameters with eval function. Snippet is given below.
Here modulename is the python file name that i had taken from user and importing as module
exec("import "+modulename)
result = eval(f"{modulename}.{somefunctionname}(arguments)")
Which is working absolutely fine. But i know this is not the secured approach.
My question , Is there any other way through which i can run users file securely as the method that i am using is not secure ? I know the proposed solutions can't be full proof but what are the other ways in which i can run this (like if it can be solved with dockerization then what will be the approach or some external tools that i can use with API )?
Or if possible can somebody tell me how can i simply sandbox this or any tutorial that can help me..?
Any reference or resource will be helpful.
It is an important question. In python sandboxing is not trivial.
It is one of the few cases where the question which version of python interpreter you are using. For example, Jyton generates Java bytecode, and JVM has its own mechanism to run code securely.
For CPython, the default interpreter, originally there were some attempts to make a restricted execution mode, that were abandoned long time ago.
Currently, there is that unofficial project, RestrictedPython that might give you what you need. It is not a full sandbox, i.e. will not give you restricted filesystem access or something, but for you needs it may be just enough.
Basically the guys there just rewrote the python compilation in a more restricted way.
What it allows to do is to compile a piece of code and then execute, all in a restricted mode. For example:
from RestrictedPython import safe_builtins, compile_restricted
source_code = """
print('Hello world, but secure')
"""
byte_code = compile_restricted(
source_code,
filename='<string>',
mode='exec'
)
exec(byte_code, {__builtins__ = safe_builtins})
>>> Hello world, but secure
Running with builtins = safe_builtins disables the dangerous functions like open file, import or whatever. There are also other variations of builtins and other options, take some time to read the docs, they are pretty good.
EDIT:
Here is an example for you use case
from RestrictedPython import safe_builtins, compile_restricted
from RestrictedPython.Eval import default_guarded_getitem
def execute_user_code(user_code, user_func, *args, **kwargs):
""" Executed user code in restricted env
Args:
user_code(str) - String containing the unsafe code
user_func(str) - Function inside user_code to execute and return value
*args, **kwargs - arguments passed to the user function
Return:
Return value of the user_func
"""
def _apply(f, *a, **kw):
return f(*a, **kw)
try:
# This is the variables we allow user code to see. #result will contain return value.
restricted_locals = {
"result": None,
"args": args,
"kwargs": kwargs,
}
# If you want the user to be able to use some of your functions inside his code,
# you should add this function to this dictionary.
# By default many standard actions are disabled. Here I add _apply_ to be able to access
# args and kwargs and _getitem_ to be able to use arrays. Just think before you add
# something else. I am not saying you shouldn't do it. You should understand what you
# are doing thats all.
restricted_globals = {
"__builtins__": safe_builtins,
"_getitem_": default_guarded_getitem,
"_apply_": _apply,
}
# Add another line to user code that executes #user_func
user_code += "\nresult = {0}(*args, **kwargs)".format(user_func)
# Compile the user code
byte_code = compile_restricted(user_code, filename="<user_code>", mode="exec")
# Run it
exec(byte_code, restricted_globals, restricted_locals)
# User code has modified result inside restricted_locals. Return it.
return restricted_locals["result"]
except SyntaxError as e:
# Do whaever you want if the user has code that does not compile
raise
except Exception as e:
# The code did something that is not allowed. Add some nasty punishment to the user here.
raise
Now you have a function execute_user_code, that receives some unsafe code as a string, a name of a function from this code, arguments, and returns the return value of the function with the given arguments.
Here is a very stupid example of some user code:
example = """
def test(x, name="Johny"):
return name + " likes " + str(x*x)
"""
# Lets see how this works
print(execute_user_code(example, "test", 5))
# Result: Johny likes 25
But here is what happens when the user code tries to do something unsafe:
malicious_example = """
import sys
print("Now I have the access to your system, muhahahaha")
"""
# Lets see how this works
print(execute_user_code(malicious_example, "test", 5))
# Result - evil plan failed:
# Traceback (most recent call last):
# File "restr.py", line 69, in <module>
# print(execute_user_code(malitious_example, "test", 5))
# File "restr.py", line 45, in execute_user_code
# exec(byte_code, restricted_globals, restricted_locals)
# File "<user_code>", line 2, in <module>
#ImportError: __import__ not found
Possible extension:
Pay attention that the user code is compiled on each call to the function. However, it is possible that you would like to compile the user code once, then execute it with different parameters. So all you have to do is to save the byte_code somewhere, then to call exec with a different set of restricted_locals each time.
EDIT2:
If you want to use import, you can write your own import function that allows to use only modules that you consider safe. Example:
def _import(name, globals=None, locals=None, fromlist=(), level=0):
safe_modules = ["math"]
if name in safe_modules:
globals[name] = __import__(name, globals, locals, fromlist, level)
else:
raise Exception("Don't you even think about it {0}".format(name))
safe_builtins['__import__'] = _import # Must be a part of builtins
restricted_globals = {
"__builtins__": safe_builtins,
"_getitem_": default_guarded_getitem,
"_apply_": _apply,
}
....
i_example = """
import math
def myceil(x):
return math.ceil(x)
"""
print(execute_user_code(i_example, "myceil", 1.5))
Note that this sample import function is VERY primitive, it will not work with stuff like from x import y. You can look here for a more complex implementation.
EDIT3
Note, that lots of python built in functionality is not available out of the box in RestrictedPython, it does not mean it is not available at all. You may need to implement some function for it to become available.
Even some obvious things like sum or += operator are not obvious in the restricted environment.
For example, the for loop uses _getiter_ function that you must implement and provide yourself (in globals). Since you want to avoid infinite loops, you may want to put some limits on the number of iterations allowed. Here is a sample implementation that limits number of iterations to 100:
MAX_ITER_LEN = 100
class MaxCountIter:
def __init__(self, dataset, max_count):
self.i = iter(dataset)
self.left = max_count
def __iter__(self):
return self
def __next__(self):
if self.left > 0:
self.left -= 1
return next(self.i)
else:
raise StopIteration()
def _getiter(ob):
return MaxCountIter(ob, MAX_ITER_LEN)
....
restricted_globals = {
"_getiter_": _getiter,
....
for_ex = """
def sum(x):
y = 0
for i in range(x):
y = y + i
return y
"""
print(execute_user_code(for_ex, "sum", 6))
If you don't want to limit loop count, just use identity function as _getiter_:
restricted_globals = {
"_getiter_": labmda x: x,
Note that simply limiting the loop count does not guarantee security. First, loops can be nested. Second, you cannot limit the execution count of a while loop. To make it secure, you have to execute unsafe code under some timeout.
Please take a moment to read the docs.
Note that not everything is documented (although many things are). You have to learn to read the project's source code for more advanced things. Best way to learn is to try and run some code, and to see what kind function is missing, then to see the source code of the project to understand how to implement it.
EDIT4
There is still another problem - restricted code may have infinite loops. To avoid it, some kind of timeout is required on the code.
Unfortunately, since you are using django, that is multi threaded unless you explicitly specify otherwise, simple trick for timeouts using signeals will not work here, you have to use multiprocessing.
Easiest way in my opinion - use this library. Simply add a decorator to execute_user_code so it will look like this:
#timeout_decorator.timeout(5, use_signals=False)
def execute_user_code(user_code, user_func, *args, **kwargs):
And you are done. The code will never run more than 5 seconds.
Pay attention to use_signals=False, without this it may have some unexpected behavior in django.
Also note that this is relatively heavy on resources (and I don't really see a way to overcome this). I mean not really crazy heavy, but it is an extra process spawn. You should hold that in mind in your web server configuration - the api which allows to execute arbitrary user code is more vulnerable to ddos.
For sure with docker you can sandbox the execution if you are careful. You can restrict CPU cycles, max memory, close all network ports, run as a user with read only access to the file system and all).
Still,this would be extremely complex to get it right I think. For me you shall not allow a client to execute arbitrar code like that.
I would be to check if a production/solution isn't already done and use that. I was thinking that some sites allow you to submit some code (python, java, whatever) that is executed on the server.

Viewing Local Variables in Spyder's Variable Explorer

I'm new to python and am using Spyder's IDE. One feature I appreciate about it is it's variable explorer. However, based on some research, I found that it only shows global variables. A workaround that I found for that is by using the inspect module:
import inspect
local_vars = {}
def main():
global local_vars
a = 2
b = 4
c = a+b
local_vars = inspect.currentframe().f_locals
return c
main()
This works well, however, I have other functions that are called from within main() and I'd like to see those variables in the variable explorer as well. I mimicked what was done for the variables in the main function and the dict does not appear. I noticed that when I disable the setting to "exclude unsupported data types" in Spyder's variable explorer options, the second dict appears with the right size attribute, however, I am unable to open/view it. Any ideas on a possible work around? This is my first time posting BTW.
Thanks!!
Here is a working example of my issue and I've traced it down to pylab subplots.
import inspect, pylab
mainVars = {}
def main():
global mainVars
a = 1
b = 2
fig = pylab.figure()
subPlot = fig.add_subplot(211) ## line of interest
pylab.close('all')
mainVars = inspect.currentframe().f_locals
main()
When the line of interest is commented out, the dict is created successfully and can be viewed. It appears that the object created using fig.add_subplot() is not being handled properly by the dict. It seems to be an unsupported datatype.
Hope this helps clarify the issue.
Thanks again.
To view the contents of local variables when some of them are unsupported, you have to follow these steps:
Go to the options menu of the Variable Explorer (the last icon from left to right on it).
Select the option called Exclude unsupported data types.
Then you'll see all local variables saved in the f_locals dict, even if you're unable to double click on it.
All of these workarounds are making your code significantly harder to read for outsiders. You have two options to inspect the values of the variables inside your function. First, you could just return the variables you are interested in:
def main():
a = 2
b = 4
c = a+b
return a, b, c
a, b, c = main()
Second, if you just want to check that the function works as expected or debug it, you can debug the function and step into it. So select Run|Debug from the menu instead of running the file directly. Then you can step into the function - the values of the variables will be visible in the variable explorer when the execution is inside the function.

Categories