Python bindings allow the C++ code in ns-3 to be called from Python.
This chapter shows you how to create a Python script that can run ns-3 and also the process of creating Python bindings for a C++ ns-3 module.
The goal of Python bindings for ns-3 are two fold:
For the time being, the primary focus of the bindings is the first goal, but the second goal will eventually be supported as well. Python bindings for ns-3 are being developed using a new tool called PyBindGen (http://code.google.com/p/pybindgen).
Here is some example code that is written in Python and that runs ns-3, which is written in C++. This Python example can be found in examples/tutorial/first.py:
import ns.applications
import ns.core
import ns.internet
import ns.network
import ns.point_to_point
ns.core.LogComponentEnable("UdpEchoClientApplication", ns.core.LOG_LEVEL_INFO)
ns.core.LogComponentEnable("UdpEchoServerApplication", ns.core.LOG_LEVEL_INFO)
nodes = ns.network.NodeContainer()
nodes.Create(2)
pointToPoint = ns.point_to_point.PointToPointHelper()
pointToPoint.SetDeviceAttribute("DataRate", ns.core.StringValue("5Mbps"))
pointToPoint.SetChannelAttribute("Delay", ns.core.StringValue("2ms"))
devices = pointToPoint.Install(nodes)
stack = ns.internet.InternetStackHelper()
stack.Install(nodes)
address = ns.internet.Ipv4AddressHelper()
address.SetBase(ns.network.Ipv4Address("10.1.1.0"), ns.network.Ipv4Mask("255.255.255.0"))
interfaces = address.Assign (devices);
echoServer = ns.applications.UdpEchoServerHelper(9)
serverApps = echoServer.Install(nodes.Get(1))
serverApps.Start(ns.core.Seconds(1.0))
serverApps.Stop(ns.core.Seconds(10.0))
echoClient = ns.applications.UdpEchoClientHelper(interfaces.GetAddress(1), 9)
echoClient.SetAttribute("MaxPackets", ns.core.UintegerValue(1))
echoClient.SetAttribute("Interval", ns.core.TimeValue(ns.core.Seconds (1.0)))
echoClient.SetAttribute("PacketSize", ns.core.UintegerValue(1024))
clientApps = echoClient.Install(nodes.Get(0))
clientApps.Start(ns.core.Seconds(2.0))
clientApps.Stop(ns.core.Seconds(10.0))
ns.core.Simulator.Run()
ns.core.Simulator.Destroy()
waf contains some options that automatically update the python path to find the ns3 module. To run example programs, there are two ways to use waf to take care of this. One is to run a waf shell; e.g.:
$ ./waf shell
$ python examples/wireless/mixed-wireless.py
and the other is to use the –pyrun option to waf:
$ ./waf --pyrun examples/wireless/mixed-wireless.py
To run a python script under the C debugger:
$ ./waf shell
$ gdb --args python examples/wireless/mixed-wireless.py
To run your own Python script that calls ns-3 and that has this path, /path/to/your/example/my-script.py, do the following:
$ ./waf shell
$ python /path/to/your/example/my-script.py
Python bindings for ns-3 are a work in progress, and some limitations are known by developers. Some of these limitations (not all) are listed here.
First of all, keep in mind that not 100% of the API is supported in Python. Some of the reasons are:
Most of the missing APIs can be wrapped, given enough time, patience, and expertise, and will likely be wrapped if bug reports are submitted. However, don’t file a bug report saying “bindings are incomplete”, because we do not have manpower to complete 100% of the bindings.
Conversion constructors are not fully supported yet by PyBindGen, and they always act as explicit constructors when translating an API into Python. For example, in C++ you can do this:
Ipv4AddressHelper ipAddrs;
ipAddrs.SetBase ("192.168.0.0", "255.255.255.0");
ipAddrs.Assign (backboneDevices);
In Python, for the time being you have to do:
ipAddrs = ns3.Ipv4AddressHelper()
ipAddrs.SetBase(ns3.Ipv4Address("192.168.0.0"), ns3.Ipv4Mask("255.255.255.0"))
ipAddrs.Assign(backboneDevices)
CommandLine::AddValue() works differently in Python than it does in ns-3. In Python, the first parameter is a string that represents the command-line option name. When the option is set, an attribute with the same name as the option name is set on the CommandLine() object. Example:
NUM_NODES_SIDE_DEFAULT = 3
cmd = ns3.CommandLine()
cmd.NumNodesSide = None
cmd.AddValue("NumNodesSide", "Grid side number of nodes (total number of nodes will be this number squared)")
cmd.Parse(argv)
[...]
if cmd.NumNodesSide is None:
num_nodes_side = NUM_NODES_SIDE_DEFAULT
else:
num_nodes_side = int(cmd.NumNodesSide)
Callback based tracing is not yet properly supported for Python, as new ns-3 API needs to be provided for this to be supported.
Pcap file writing is supported via the normal API.
Ascii tracing is supported since ns-3.4 via the normal C++ API translated to Python. However, ascii tracing requires the creation of an ostream object to pass into the ascii tracing methods. In Python, the C++ std::ofstream has been minimally wrapped to allow this. For example:
ascii = ns3.ofstream("wifi-ap.tr") # create the file
ns3.YansWifiPhyHelper.EnableAsciiAll(ascii)
ns3.Simulator.Run()
ns3.Simulator.Destroy()
ascii.close() # close the file
There is one caveat: you must not allow the file object to be garbage collected while ns-3 is still using it. That means that the ‘ascii’ variable above must not be allowed to go out of scope or else the program will crash.
Python bindings do not work on Cygwin. This is due to a gccxml bug.
You might get away with it by re-scanning API definitions from within the cygwin environment (./waf –python-scan). However the most likely solution will probably have to be that we disable python bindings in CygWin.
If you really care about Python bindings on Windows, try building with mingw and native python instead. Or else, to build without python bindings, disable python bindings in the configuration stage:
$ ./waf configure --disable-python
There are currently two kinds of Python bindings in ns-3:
The process by which Python bindings are handled is the following:
If something goes wrong with compiling Python bindings and you just want to ignore them and move on with C++, you can disable Python with:
$ ./waf --disable-python
So you have been changing existing ns-3 APIs and Python bindings no longer compile? Do not despair, you can rescan the bindings to create new bindings that reflect the changes to the ns-3 API.
Depending on if you are using monolithic or modular bindings, see the discussions below to learn how to rescan your Python bindings.
To scan the monolithic Python bindings do the following:
$ ./waf --python-scan
The monolithic Python API definitions are organized as follows. For each ns-3 module <name>, the file bindings/python/ns3_module_<name>.py describes its API. Each of those files have 3 toplevel functions:
Since ns 3.11, the modular bindings are being added, in parallel to the old monolithic bindings.
The new python bindings are generated into an ‘ns’ namespace, instead of ‘ns3’ for the old bindings. Example:
from ns.network import Node
n1 = Node()
With modular Python bindings:
To scan the modular Python bindings for the core module, for example, do the following:
$ ./waf --apiscan=core
To scan the modular Python bindings for all of the modules, do the following:
$ ./waf --apiscan=all
If you are adding a new module, Python bindings will continue to compile but will not cover the new module.
To cover a new module, you have to create a bindings/python/ns3_module_<name>.py file, similar to the what is described in the previous sections, and register it in the variable LOCAL_MODULES() in bindings/python/ns3modulegen.py
To add support for modular bindings to an existing ns-3 module, simply add the following line to its wscript build() function:
bld.ns3_python_bindings()
The src/<module>/bindings directory may contain the following files, some of them optional:
If you are a developer and need more information on ns-3‘s Python bindings, please see the Python Bindings wiki page.