Page tree
Skip to end of metadata
Go to start of metadata

About

This module allows one to write FreeSWITCH applications using Python.


 Click here to expand Table of Contents

 

Install & Configure

Building mod_python

Install python-dev package on Debian/Ubuntu:

Enable compilation in modules.conf:

  • In the FreeSWITCH source folder, edit modules.conf and uncomment languages/mod_python

Recompile and install FreeSWITCH:

The configure script will try to detect your existing python version. If it cannot find it or it does not support multi-threading, it will print a warning message.

You can specify an argument to configure to make it use a particular version:

Make current auto fixes the makefile in case it doesn't get generated (correctly, or at all). make python-reconf to regenerate it.

Enabling mod_python

Open up conf/autoload_configs/modules.conf.xml and add an entry:

Finding Python modules

There are two different ways to tell the python interpreter how to find python modules. If you don't do either of these, the embedded Python interpreter will have no way to find your python scripts.

Assuming you have:

This is telling python to load the bar module that lives in the foo package.

Copy or Symlink to site-packages directory, i.e if the source file is in /usr/src/foo/bar.py:

The same can be done via copying. Don't forget, the foo package directory will need an __init__.py.

Adding to PYTHONPATH environment variable, if the file is in /usr/src/foo/bar.py, add the following to your system environment startup

Don't forget, the foo package directory will need an __init__.py.

In the shell where freeswitch is started, this environment variable will need to be defined.

Invoking mod_python applications

To call a Python application from dialplan, you should probably be familiar with the Dialplan. You simply call it as an application similar to:

The module is bar, in the foo package. See the Finding python modules section to tell the embedded python interpreter how to find this module.

If your module (say, test.py) is not in any package directory, then you would instead use:

In both cases, you need to leave off the .py file extension otherwise it will not work. It expects a fully qualified module name only.

You would put this in your 'dialplan' if using the XML dialplan module with Freeswitch. Don't forget your 'condition' tags and all that goodness.

It's possible to call a Python script from the CLI with the python command:

if you invoke it this way, your python handler() function will be called with no arguments.

Python module specification

Your python module must define a function called handler that takes two arguments: session and args.

The session is the main interface to Freeswitch, which wraps a Freeswitch session, and the args are any args passed to the handler script.

API

Supports the same API as mod_lua

Sample Python Scripts

Hello World via call

In this example, if you copied that module to your site-packages directory and called it from an extension in the dialplan, you'd be using the `handler` function.

Here is a copy of the python_example module that comes in the FreeSWITCH source code. See latest version here.

FreeSWITCH's mod_python usage examples  Expand source

Hello World - Dialplan API

So foo channel variable is set to the output of the my_script python script.

The script will be called with a magic object called "stream" which has the method write, and the anything written to this method will be the script's output. So for example

will cause the foo variable to be set to "hello".

Run something in a thread using API

Usage of the API in Python is conceptually identical to usage in other supported languages.

The example scripts below employ the API, along with the 'runtime' function described in python_example.py to run a job in a thread.

This approach provides a means of implementing non-blocking code without employing mod_event_socket and may be suitable/useful for cleanup or post-processing.

This example includes two modules. The first module is a virtual copy of the default example script with a couple of notable differences.

 Expand source
 
The second module, "postprocessing", handles our post-processing needs and for convenience resides in the same package, "foo":

When running the above example, the client should receive a hangup immediately after streamFile returns. 10 seconds later the "Caller: xxxx hung up 10s ago!" message should be printed to the console.

Fetch Effective Caller Name from CSV file using Python

The purpose of this script is simply to associate foreign caller-id without caller_id_name to a static caller_id_name using a csv file, and at the same time it shows how to work with Python and CSV files inside FreeSWITCH

First you must add something like that to the generic dialplan for your local extensions (personally i use Local_Extension in dialplan/default.xml:

It's pretty self-explanatory, it create a variable which will be used in the python script that we call on the second line. setCallerName is the name of your script in ${FS_ROOT}/scripts/

Here the code in ${FS_ROOT}/scripts/setCallerName.py :

The CSV should look something like this :

FAQ

Does each script spawn a python interpreter?

No. A single python interpreter is spawned at module startup and used for the lifetime of the freeswitch process.

Are there thread safety issues?

Each thread swaps in its "thread state" before executing python code and then swaps it out when finished. Also during blocking calls into freeswitch, a thread will swap out its thread state in order to not block other threads, and then swap it in after the blocking call to freeswitch has finished.

I changed a module I'm importing, and nothing happened

Answer: assume you are importing a module called baz, change your entry point module to:

How do I pass arguments to the script?

This is possible using channel variables. In the dialplan:

and in the python script:

Can I test scripts using python shell?

No, it will fail as follows when you try to import the python module:

Can it serve configuration (like Lua)?

Yes, this has been added but not documented. The default hook for serving a config is xml_fetch as stated in the python_example.py script. However, the interpreter complains with a 'takes exactly 1 argument (2 given)' message if xml_fetch is defined to accept only one param. Altering the definition to accept 2 params solves the problem. However, consoleLog invariably shows param2 to contain nothing...

and you will need to create/edit a python.conf.xml to be something like:

Gotchas

String Substitution in Functions

The session function recordFile cannot contain anything but strings in the <filename> parameter. Attempting to use sql rows or other tuples as part of a string substitution will give you a

NotImplementedError: Wrong number of arguments for overloaded function 'CoreSession_recordFile'.

For example:

 

will throw the error mentioned above even though when printing the variable record_file will result in the expected value. Instead the values row[0] and row[1] need to be wrapped in the str() function when assigning a value to record_file:

should give proper behavior.

Bridge and transfer the call

If you want to originate call and bridge them directly..

Troubleshooting

Cannot import freeswitch

Copy freeswitch.py from the python directory installed by freeswitch to /usr/lib/python2.X/{dist,site}-packages

NOTE: if you are getting this error trying to test on the python shell .. you will never get past it. The only way to test python IVR scripts is to define the script in the dialplan and call the number. There is no way to currently do any testing with mod_python scripts outside the context of an IVR running in freeswitch.

Message: 'module' object has no attribute 'EventConsumer_node_set'

If you see:

Message: 'module' object has no attribute 'EventConsumer_node_set'

You upgraded your freeswitch but obviously you forgot to update freeswitch.py within the /usr/lib/pythonX.Y/site-packages/ (X and Y are the python's version)

CoreSession_streamfile() takes exactly 3 arguments (4 given)

If you see

TypeError: CoreSession_streamfile() takes exactly 3 arguments (4 given)

it could mean that you are trying to use a dtmf callback that is a bound method of an object -- don't do that! The dtmf callback function should always be at the module scope and not take ("self") as an argument.

Error calling DTMF callback - wrong # of arguments

Same as above. You may be trying to use a bound instance method to a class. (eg, takes self as first argument). This will not work. Instead what you can do is to have a nested method for your dtmf handler that can access instance's self name.

'consoleLog', argument 2 of type 'char *'

If you see error messages:

TypeError: in method 'console_log', argument 2 of type 'char *'

You just need to call str() on the variable before passing to console_log, which cannot deal with Unicode strings at the present time.

Channels are not being cleaned up

This should not happen, if it does please report a bug with detailed instructions on how to reproduce. This has surfaced and been fixed a few times.

Avoid module-level global variables

If you find yourself using the globals keyword -- redesign your script. Concurrent calls will also be looking at the same variables, and things might not work as you expected. (These aren't "thread safety" issues, per se, since the Python GIL ensures only one thread can run python code at any given time, but just be aware that multiple threads can see/access these variables).

Build error: Python.h: No such file or directory

If you see this:

 freeswitch_python.h:5:20: error: Python.h: No such file or directory

You need to install the python-dev package. You should also double-check the src/mod/languages/mod_python/Makefile to make sure it's using the version of python you are expecting. If not, you can edit the Makefile manually.

Hangup hook + SQLAlchemy crashes switch

It's not clear if it only happens in conjunction with SQLAlchemy, but removing the hangup hook definitely fixed the problem. Hangup hooks are buggy, please avoid or use with extreme caution.

 

mod_python error: mod_python.c:293 Error calling python script

If you see this:

 [ERR] mod_python.c:293 Error calling python script
Message: expected string or Unicode object, NoneType found
or
Message: expected string or Unicode object, bool found

Check your dtmf_process function. If you used "session.setInputCallback()" function, check your callback function. It should return one of these strings: "true", "false" or "pause". Forgetting to return, or using return True or return False (as a boolean) will cause the error above.

See https://jira.freeswitch.org/browse/FS-1414