how to define functions in ipython configuration file? - python

Using ipython 0.11 if I type a function definition, like
def f(s): print s
then I can use that function in that ipython session, but I don't know how to define that
in the ipython_config.py file.
If I just type the function definition in the file and try to use the function it it undefined.
Any idea?

Two answers here:
First, for super simple functions like the one above, you can define them in exec_lines, e.g.:
c.InteractiveShellApp.exec_lines = [ "def f(s): print s" ]
(you can define arbitrarily complex functions this way, but it gets annoying beyond a couple of lines)
For more complicated code, you can write a script that you would like to run at startup, and add that to the exec_files list:
c.InteractiveShellApp.exec_files = [ "/path/to/myscript.py" ]
# if you put the script in the profile dir, just the filename will suffice
We realized this is slightly annoying, so in 0.12, there will be a startup folder in profile directories, and anything you put in there will be run automatically. This is essentially adding an extra glob.glob('*.py') to exec_files (which you can do yourself in 0.11, of course).

You'll need to define a python file to run when ipython starts. You can do this by setting the exec_files list:
c = get_config()
c.InteractiveShellApp.exec_files = [
'/tmp/mymodule.py'
]
Then my file "/tmp/mymodule.py":
def foo():
print "bar"
And finally, using this:
$ ipython
In [1]: foo()
bar
More information about the ipython config-file can be found here: http://ipython.org/ipython-doc/dev/config/ipython.html

Multi-line functions can be defined in *.py and *.ipy files under the startup directory within the profile directory.
(Note that *.ipy and *.py files are treated differently.)
The profile directory can be discovered from the command line using
$ ipython locate profile

Related

Problems passing variables into IPython magic functions

While in a Jupyter Notebook I am trying to save the result of the IPython %%prun magic function to a specific file which lives in a subfolder of the current directory, say filename = r'subfoler\profile_result.txt'. I would like to be able to link this file name via
%%prun -T filename
...
However, when I do this, it just prints the profiler output to a file called filename in the current directory. This question has a similar problem, for which the solution is to pass in the variable via $filename. This does not work, and instead just saves the output to a file called $filename. Is this a bug, is it not possible to do what I want, or am I incorrectly passing in the python variable?
%prun docs has this note:
.. versionchanged:: 7.3
User variables are no longer expanded,
the magic line is always left unmodified.
I'm not sure where the expansion is normally done, but prun now uses
opts, arg_str = self.parse_options(parameter_s, 'D:l:rs:T:q',
list_all=True, posix=False)
to handle the arguments.
%history on the other hand uses
#magic_arguments
#argument...
args = parse_argstring(self.history, parameter_s)
Without getting into details, that's a different parsing route.
I have version IPython 7.16.1
%debug, %time and %timeit have the same note.
The relevant pull-request: https://github.com/ipython/ipython/pull/11516/
The reasoning: "Most useful for magics that take Python code on the line, e.g. %timeit, etc."

Apply function decorator on print function across all files without having to import and/or reapply?

Edit: My first attempt at asking this might be a bit unfocused/poorly worded here's a better explanation of what I'm trying to do:
I'm trying to modify the default behavior of the print function for the entire environment python is running in without having to modify each file that's being run.
I'm attempting to decorate the print function (I know there are many ways to do this such as overriding it but that's not really the question I'm asking) so I can have it print out some debugging information and force it to always flush. I did that like so:
def modify_print(func):
# I made this so that output always gets flushed as it won't by default
# within the environment I'm using, I also wanted it to print out some
# debugging information, doesn't really matter much in the context of this
# question
def modified_print(*args,**kwargs):
return func(f"some debug prefix: ",flush=True,*args,**kwargs)
return modified_print
print = modify_print(print)
print("Hello world") # Prints "some debug prefix Hello World"
However what I'm trying to do is modify this behavior throughout my entire application. I know I can manually decorate/override/import the print function in each file however I'm wondering if there is some way I can globally configure my python environment to decorate this function everywhere. The only way I can think to do this would be to edit the python source code and build the modified version.
EDIT:
Here's the behavior I wanted implemented, thank you Match for your help.
It prints out the line number and filename everywhere you call a print function within your python environment. This means you don't have to import or override anything manually in all of your files.
https://gist.github.com/MichaelScript/444cbe5b74dce2c01a151d60b714ac3a
import site
import os
import pathlib
# Big thanks to Match on StackOverflow for helping me with this
# see https://stackoverflow.com/a/48713998/5614280
# This is some cool hackery to overwrite the default functionality of
# the builtin print function within your entire python environment
# to display the file name and the line number as well as always flush
# the output. It works by creating a custom user script and placing it
# within the user's sitepackages file and then overwriting the builtin.
# You can disable this behavior by running python with the '-s' flag.
# We could probably swap this out by reading the text from a python file
# which would make it easier to maintain larger modifications to builtins
# or a set of files to make this more portable or to modify the behavior
# of more builtins for debugging purposes.
customize_script = """
from inspect import getframeinfo,stack
def debug_printer(func):
# I made this so that output always gets flushed as it won't by default
# within the environment I'm running it in. Also it will print the
# file name and line number of where the print occurs
def debug_print(*args,**kwargs):
frame = getframeinfo(stack()[1][0])
return func(f"{frame.filename} : {frame.lineno} ", flush=True,*args,**kwargs)
return debug_print
__builtins__['print'] = debug_printer(print)
"""
# Creating the user site dir if it doesn't already exist and writing our
# custom behavior modifications
pathlib.Path(site.USER_SITE).mkdir(parents=True, exist_ok=True)
custom_file = os.path.join(site.USER_SITE,"usercustomize.py")
with open(custom_file,'w+') as f:
f.write(customize_script)
You can use usercustomize script from the site module to achieve something like this.
First, find out where your user site-packages directory is:
python3 -c "import site; print(site.USER_SITE)"
/home/foo/.local/lib/python3.6/site-packages
Next, in that directory, create a script called usercustomize.py - this script will now be run first whenever python is run.
One* way to replace print is to override the __builtins__ dict and replace it with a new method - something like:
from functools import partial
old_print = __builtins__['print']
__builtins__['print'] = partial(old_print, "Debug prefix: ", flush=True)
Drop this into the usercustomize.py script and you should see all python scripts from then on being overridden. You can temporarily disable calling this script by calling python with the -s flag.
*(Not sure if this is the correct way of doing this - there may be a better way - but the main point is that you can use usercustomize to deliver whatever method you choose).
There's no real reason to define a decorator here, because you are only intending to apply it to a single, predetermined function. Just define your modified print function directly, wrapping it around __builtins__.print to avoid recursion.
def print(*args, **kwargs):
__builtins.__print(f"some debug prefix: ", flush=True, *args, **kwargs)
print("Hello world") # Prints "some debug prefix Hello World"
You can use functools.partial to simplify this.
import functools
print = functools.partial(__builtins.__print, f"some debug prefix: ", flush=True)

Python interpreters and Alias's

Is there any mechanism similar to alias's (with something like BASH) that can be used in Ipython or the Python Interpreter?
For instance if I find myself frequently doing something like:
var = urllib2.urlopen('http://programmers.stackexchange.com')
But I don't want to continually type out those strings.
Is there any method of (Persistently between exits) shortening the request other than writing a script for it?
No, but in your interpreter, write this:
def pse_url():
global var
var = urllib2.urlopen('http://programmers.stackexchange.com')
Then, write pse_url() whenever you need to affect your variable.
It would be cleaner to not use a global variable:
var = pse_url()
If you have many such utilities, put them in your own module and load them once when you start the REPL.
My guess is for such one line expressions you can create lambdas functions ( and functions for bigger one as #coredump suggested), see code below:
se_open = (lambda: urllib2.urlopen('http://programmers.stackexchange.com'))
so_open = (lambda: urllib2.urlopen('http://programmers.stackexchange.com'))
Know if you have to create new var you have to simply run command:
var_se = se_open()
var_so = so_open()
Also you can create script which contains all thst shortcuts and start python with imported script by command:
$ python -i script.py
All functions defined in script.py would be available in your REPL.

Calling python functions without running from the editor

Please excuse what I know is an incredibly basic question that I have nevertheless been unable to resolve on my own.
I'm trying to switch over my data analysis from Matlab to Python, and I'm struggling with something very basic: in Matlab, I write a function in the editor, and to use that function I simply call it from the command line, or within other functions. The function that I compose in the matlab editor is given a name at the function definition line, and it's generally best for the function name to match the .m file name to avoid confusion.
I don't understand how functions differ in Python, because I have not been successful translating the same approach there.
For instance, if I write a function in the Python editor (I'm using Python 2.7 and Spyder), simply saving the .py file and calling it by its name from the Python terminal does not work. I get a "function not defined" error. However, if I execute the function within Spyder's editor (using the "run file" button), not only does the code execute properly, from that point on the function is also call-able directly from the terminal.
So...what am I doing wrong? I fully appreciate that using Python isn't going to be identical to Matlab in every way, but it seems that what I'm trying to do isn't unreasonable. I simply want to be able to write functions and call them from the python command line, without having to run each and every one through the editor first. I'm sure my mistake here must be very simple, yet doing quite a lot of reading online hasn't led me to an answer.
Thanks for any information!
If you want to use functions defined in a particular file in Python you need to "import" that file first. This is similar to running the code in that file. Matlab doesn't require you to do this because it searches for files with a matching name and automagically reads in the code for you.
For example,
myFunction.py is a file containing
def myAdd(a, b):
return a + b
In order to access this function from the Python command line or another file I would type
from myFunction import myAdd
And then during this session I can type
myAdd(1, 2)
There are a couple of ways of using import, see here.
You need to a check for __main__ to your python script
def myFunction():
pass
if __name__ == "__main__":
myFunction()
then you can run your script from terminal like this
python myscript.py
Also if your function is in another file you need to import it
from myFunctions import myFunction
myFunction()
Python doesn't have MATLAB's "one function per file" limitation. You can have as many functions as you want in a given file, and all of them can be accessed from the command line or from other functions.
Python also doesn't follow MATLAB's practice of always automatically making every function it can find usable all the time, which tends to lead to function name collisions (two functions with the same name).
Instead, Python uses the concept of a "module". A module is just a file (your .py file). That file can have zero or more functions, zero or more variables, and zero or more classes. When you want to use something from that file, you just import it.
So say you have a file 'mystuff.py':
X = 1
Y = 2
def myfunc1(a, b):
do_something
def myfunc2(c, d):
do_something
And you want to use it, you can just type import mystuff. You can then access any of the variables or functions in mystuff. To call myfunc2, you can just do mystuff.myfunc2(z, w).
What basically happens is that when you type import mystuff, it just executes the code in the file, and makes all the variables that result available from mystuff.<varname>, where <varname> is the name of the variable. Unlike in MATLAB, Python functions are treated like any other variable, so they can be accessed just like any other variable. The same is true with classes.
There are other ways to import, too, such as from mystuff import myfunc.
You run python programs by running them with
python program.py

Cmd module '~' completion

I have been playing around with the cmd python module and was looking at the text completion function. I have been trying to get it to expand/recognise '~' to my home directory but with no avail.
I've noticed that I can handle default completion by overriding the completedefault(self, *ignored) method from the cmd module. Where ignored is a tuple of the text, line, begidx, endidx. If I type in the command my_command ./folder the text parameter will be './folder' and this mean I can do something like: glob.glob(text + '*') which returns a list of all the files in that folder. However, if I now do my_command ~/folder the text variable now only contains /folder, so I am unable to use os.path.expanduser(text) to determine the absolute path of that folder and show all the files in that folder.
Basically I am wondering if someone could point me in the right direction in order to expand paths with a ~ in it.
Expanding on the answer from: https://stackoverflow.com/a/6657975/1263565
You could override the cmd module's completedefault() method with:
def completedefault(self, *ignored):
# Set the autocomplete preferences
readline.set_completer_delims(' \t\n;')
readline.parse_and_bind("tab: complete")
readline.set_completer(complete)
with the complete method looking like:
def complete(text, state):
return (glob.glob(os.path.expanduser(text)+'*')+[None])[state]
This should now allow ~ expansion.

Categories

Resources