An Advanced Primer — Python, Libraries, Versions, and the Future
Ignition provides users with the ability to do Python scripting as a core part of the Ignition platform. Ignition and Ignition Edge (with Compute) both provide the environment for advanced scripting and most Ignition customers use Python in their projects in some manner.
What’s available out-of-the-box?
Ignition includes Python by using a well-respected project called Jython, which is used in other products as well, like IBM Websphere. Jython’s current release is Jython 2.7 and includes most of the Python Standard Library. Inductive Automation has created a rich scripting API with well over 300 script functions with documentation and examples. In addition, Jython has a unique ability to import Java classes, which makes all classes in the Java Class Library available, along with a large variety of other Java libraries that are included in the Ignition platform.
What is Python, and what are CPython and Jython?
Python is a language specification. If you learn the Python programming language, and you write some Python code, you then need somewhere to run your Python code.
CPython is a program (“interpreter”) that you can run to execute your Python code. It’s written in C, hence the name “CPython.” Unfortunately, CPython is normally referred to as “Python,” as it was the first interpreter written, and in fact, when you “download Python” from python.org, it gives you CPython. Online articles talking about “Python” may be talking about the Python language or the CPython interpreter, and you have to think through how “Python” is used in any given sentence to know which one the author means.
Jython is another interpreter that can execute your Python code. It’s written in Java, which enables it to access the Java class library and to easily run cross-platform. Ignition’s Jython implementation makes it simple and performant to access Ignition’s systems through Ignition’s scripting API.
Why does Ignition use Python 2.7? Isn’t 2.7 no longer supported?
CPython 2.7 has been retired. You can find more information about that here: Sunsetting CPython. For a good part of that page, these folks are specifically talking about the CPython interpreter.
Jython 2.7 is still under development and continues to have new versions and bug fixes released. Inductive Automation also maintains our codebase of Jython and occasionally applies bug fixes as well as the need arises. This means that Ignition’s Python 2.7 is still supported and will continue to be in the future. CPython’s sunsetting doesn’t affect Ignition.
Jython 3 is the next major release of Jython, but it isn’t available for public release yet and is likely a few years away from release. Python 3 is also a different language from Python 2, as the Python language has some changes that require adjustments/rewrites of Python 2 code to run in Python 3. For that reason, Inductive Automation currently expects that Ignition will continue to support Python 2 going into the future, even after Jython 3 is released. It’s possible Ignition will support Python 2 and Python 3 simultaneously at some point in the future.
What about Python libraries?
Python libraries (most of which can be found at pypi.org) are a mixture of Python code and C code. Because CPython is based on C, the CPython interpreter is written to be able to access native C libraries.
Because Ignition’s Python interpreter is Jython, that means unlike CPython, Java libraries are accessible. It also means C libraries aren’t accessible.
Libraries written in pure Python are generally available in Jython. Since the majority of the most popular Python libraries do require C libraries, the selection of Python libraries that can be used in Ignition directly is fairly limited. For example, numpi, scipi, pandas, and scikit-learn are all unavailable. However, libraries intended for the web and other less data-intensive applications sometimes work. Requests is a good example of a versatile REST library that you can import into Ignition.
If you want to import libraries, drop them into Ignition’s user-lib/pylib folder. They’ll be automatically accessible on the Gateway and also automatically distributed to any open clients.
Jython ships with a Python package management tool called pip. There are a few more steps necessary to get pip up and running than there used to be, due to some recent changes from pypi.org that require something called SNI.
- Install Java 8 or 11 if you haven’t done so already. (Azul Zulu link here)
- Use the Jython Installer to install a local copy of Jython. (Double click installer, or java -jar jython-installer-2.7.2.jar)
- Install pip in Jython by navigating to <jython-dir>/bin and running ‘jython -m ensurepip’
- Update Jython’s ssl.py to apply a bug fix with SNI support.
- Download the updated ssl.py.
- Replace the existing ssl.py with that one at <jython-dir>/Lib/ssl.py
- Install packages with pip (‘jython -m pip install <package>’)
- Then copy all contents from your local Jython installation’s site-packages/ directory to Ignition’s user-lib/pylib/site-packages.
- As a side note, keep pip at the current version, and don’t upgrade it to the latest version. (Upgrading to the latest requires JNA libraries, which Jython doesn’t ship with by default, and the pip version that comes with Jython works well.) If you accidentally upgrade pip, you’ll get a “No module named _winreg” issue when trying to install new packages. To restore the prior version, just remove the pip directories from site-packages and re-run ‘jython -m ensurepip’.
As previously mentioned, most Python packages won’t work with Jython since they have C dependencies, but a number of them do work, so feel free to give pip a shot if desired.
Can I run Python 3 code from Ignition?
Sure, you can invoke code written for Python 3 from inside Ignition. You can’t run that code directly inside Ignition, so Ignition’s scripting API wouldn’t be available, and your Python 3 code won’t be saved with any standard Ignition Gateway Backups, but we have plenty of customers who are doing machine learning, advanced data analytics, and other functions in Python 3 code that runs alongside Ignition.
There are two ways to do this.
The cleanest approach is to wrap your Python 3 code with Flask or Bottle, which makes it available by a simple REST call from Ignition.
A simple Bottle example:
from bottle import route, run
return "Hello World!"
run(host='localhost', port=8080, debug=True)
result = system.net.httpClient().get(“http://localhost:8080/hello”)
A simple Flask example:
from flask import Flask app = Flask(__name__) @app.route("/hello") def hello_world(): return "Hello, World!"
Flask is more popular than Bottle, but requires a few more steps to run. Follow the tutorial on the Flask website.
result = system.net.httpClient().get(“http://localhost:5000/hello”)
After doing that, you’ll need to set up your Python program as a service on the OS you’re running. There are many guides available online for this step. (In Windows, you can use Python’s pywin32 library. On Linux, this can be done by creating a service for systemd.)
Another approach is to invoke the Python script directly from Ignition. This is less portable and scalable, and doesn’t provide a strong separation between your Python 3 program and Ignition, but it is simpler to configure and avoids the need to run an additional service.
Keep in mind that going this direction may not return information on Exceptions or errors like Flask or Bottle will provide, unless you specifically write your code to process them. You’re also limited to send around 32kB of string characters in the arguments in Windows, and the limit varies on Linux and macOS.
pythonPath = "C:/Program Files/Python310/python.exe"
scriptPath = "C:/test.py"
param1 = "Motor"
param2 = "243"
result = subprocess.check_output([pythonPath, scriptPath, param1, param2])
Note that parameters will always need to be sent as strings, and the results will need to be sent back as strings from the Python 3 script as well. If using any Python objects in Ignition, system.util.jsonEncode() can often be used to change those Python objects into json strings. When receiving the results back, if the results are a json string, system.util.jsonDecode() can be used to change that string back into an object in Ignition.
How can I include Java libraries?
Including external Java libraries that can be accessed from Python is a great way to extend functionality. Writing a simple module and exposing scripting functions is fairly easy to do if you have development experience. Before doing this, we recommend at least having an Inductive University credential and having some Java experience.
Note that Module Development is done in Java, and isn’t an activity that Inductive Automation’s technical support directly supports. The SDK Documentation can be very useful, and if you’re already versed in Java and have done some troubleshooting on your own first and are still having issues, the Inductive Automation Module Development Forum can sometimes provide assistance as well.