Search by Tags

Python in Linux

 

Article updated at 28 Mar 2022
Compare with Revision




Introduction

Python is a widely used interpreted language. Despite the performance gap when compared to a compiled solution, it is still suitable for some applications.

This article focuses on the BSP Layers and Reference Images for Yocto Project. If you want to learn how to use Python on Torizon, read the article Python development on TorizonCore.

Python Setup

While Building a Linux Image

Note: Python might be already added in certain versions of our Reference Multimedia Image: BSP Layers and Reference Images for Yocto Project Software. You can quickly check by running the python command on the command line.

A set of python packages can be added to an OpenEmbedded Linux build for inclusion into the image's root file system. After establishing the image build configuration, additionally append the following line to the oe-core/build/conf/local.conf file:

local.conf
IMAGE_INSTALL_append = "python"

You may also want to install other packages as python-pip and dependencies to the Python packages you plan to use.

Using existing packages

You can search related Python packages using the OpenEmbedded recipes index or by searching the layers directory of your build environment. An example is provided to search for the pyserial package:

user@host:~$ cd <openembedded-setup-directory>
user@host:~/oe-core$ ls
build deploy export layers
user@host:~/oe-core$ find ./layers -name "*python*pyserial*bb"
./layers/meta-openembedded/meta-python/recipes-devtools/python/python3-pyserial_3.4.bb
./layers/meta-openembedded/meta-python/recipes-devtools/python/python-pyserial_3.4.bb

Writing your own recipes

Even if you are starting with OpenEmbedded, writing recipes for Python modules is usually straightforward. There are many recipes out there that do it for you to use as examples and their structure looks very alike.

A possible path for writing a recipe is provided below. We use the TinyDB 3.7.0 release for illustration:

Before writing the recipe

  1. Go to the Python Package Index and find the package you are after. In our example TinyDB.
  2. Find the downloads section, copy the download link and download the package to your PC. In our case, tinydb downloads section.
  3. Find the license type. In our case is MIT.
  4. Generate the md5 and sha256 checksums for the compressed package. In our case md5:e28a5650ef8796ab6b7892591edfbae8 and sha256:071105c339b44f928968cb4e62f7340a568beb9962a3dcda66eb82da9cab0b0a
  5. Unpack the compressed package and search for the license file - common namings are README or LICENSE, but it may be a different name. In our example the name of the license file is PKG-INFO.
  6. Generate the md5sum for the license file. For the .tar.gz compressed release of TinyDB 3.7.0, it is 8e3a385933697d0374af36db8ee0319d.
  7. Try to understand which are the package dependencies. You can do this empirically once you have written the recipe.

Writing the recipe

We will create three files:

Note: Follow the dash, underscore and case standard provided, since those matter for OpenEmbedded. See the project documentation for further details.

  • package-name.inc: this is common information to be used in Python 2 and Python 3 recipes.
  • python-package-name_version.bb: recipe to build Python 2 module.
  • python3-package-name_version.bb: recipe to build Python 3 module.

For our example:

  • python-tinydb.inc
  • python-tinydb_3.7.0.bb
  • python3-tinydb_3.7.0.bb

First create a new layer (recommended) to hold all your OpenEmbedded customizations, or you can do it in an existing layer also for studying purposes.

# assuming you have a meta-mylayer already setup and working.
user@host:~/oe-core$ mkdir -p layers/meta-mylayer/recipes-devtools/python-tinydb
user@host:~/oe-core$ cd layers/meta-mylayer/recipes-devtools/python-tinydb

Create the files provided below:

python-tinydb.inc
# Add the package description here. DESCRIPTION = "TinyDB is a lightweight document oriented database optimized \ for your happiness :) It's written in pure Python and has no external \ dependencies. The target are small apps that would be blown away by a SQL-DB \ or an external database server." SECTION = "devel/python" # Add the license type found in step 3 of previous section LICENSE = "MIT" # Add the license md5sum found in step 6 of previous section LIC_FILES_CHKSUM = "file://${S}/PKG-INFO;md5=8e3a385933697d0374af36db8ee0319d" PYPI_PACKAGE = "tinydb" PYPI_PACKAGE_EXT = "tar.gz" BBCLASSEXTEND = "native" # Add the compressed package md5 and sha256 checksums generated in step 4 of previous section SRC_URI[md5sum] = "e28a5650ef8796ab6b7892591edfbae8" SRC_URI[sha256sum] = "071105c339b44f928968cb4e62f7340a568beb9962a3dcda66eb82da9cab0b0a" inherit pypi
python-tinydb_3.7.0.bb
inherit setuptools require python-tinydb.inc # Uncomment the lines below and add runtime dependencies if any. Our example does not have any dependencies. #RDEPENDS_${PN} += "" #RDEPENDS_${PN}_class-native = ""
python3-tinydb_3.7.0.bb
inherit setuptools3 require python-tinydb.inc # Uncomment the lines below and add runtime dependencies if any. Our example does not have any dependencies. #RDEPENDS_${PN} += "" #RDEPENDS_${PN}_class-native = ""

Now you can add the package to your image, e.g. for TinyDB:

local.conf
IMAGE_INSTALL_append = "python-tinydb" # or IMAGE_INSTALL_append = "python3-tinydb"

Performance and suitability

Tests results are pending; however, Python is expected to perform at least an order of magnitude slower than equivalent compiled C/C++ code due to Python's run-time interpreted nature. Use of a Just-in-Time or Ahead-of-Time compiler should improve performance; however, such compilers have not been tested with our Linux images.

Regardless, Python is still a viable alternative for performing lighter processing tasks. It can be effective for low level IO operations as well as high level applications such as web services. Furthermore, Python code can be rapidly modified and tested without the need to recompile.

Examples

GPIO - Blinking LED

Blinking a LED using GPIO access through sysfs.

#!/usr/bin/env python
 
import time
import os.path
import traceback
 
GPIO_RESET    = False;  # Whether GPIOs should be re-exported
GPIO_PATH     = "/sys/class/gpio";
GPIO_DIR_OUT  = "out";
GPIO_VAL_HI   = "1";
GPIO_VAL_LO   = "0";
GPIO_CHAN_NUM = "146";  # GPIO1 on Apalis T30
 
BLINK_PERIOD  = 500;  # Blink period (milliseconds)
BLINK_DUTY    = 0.25; # Blink duty cycle (fraction)
 
def main():
    try: 
        ### Initialize GPIO - optionally reset if already initialized
 
        ## Note: GPIOs which are already used in the drivers can not be controlled from sysfs, 
        ## unless a driver explicitly exported that particular pins GPIO.
 
        # Open GPIO export & unexport files
        exportFile = open(GPIO_PATH+'/export', 'w')
        unexportFile = open(GPIO_PATH+'/unexport', 'w')
 
        # Unexport GPIO if it exists and GPIO_RESET is enabled
        exportExists = os.path.isdir(GPIO_PATH+'/gpio'+GPIO_CHAN_NUM)
        if exportExists and GPIO_RESET:
            unexportFile.write(GPIO_CHAN_NUM)
            unexportFile.flush()
 
        # Export GPIO
        if not exportExists or GPIO_RESET:
            exportFile.write(GPIO_CHAN_NUM)
            exportFile.flush()
 
        # Open GPIO direction file to set direction
        directionFile = open(GPIO_PATH+'/gpio'+GPIO_CHAN_NUM+'/direction','w')
 
        # Set GPIO direction to "out"
        directionFile.write(GPIO_DIR_OUT)
        directionFile.flush()
 
        # Open GPIO value file to set value
        valueFile = open(GPIO_PATH+'/gpio'+GPIO_CHAN_NUM+'/value','w')
 
        # Loop indefinitely
        while True:
 
            # Set GPIO value to HI
            valueFile.write(GPIO_VAL_HI)
            valueFile.flush()
 
            # Sleep for blink on duration
            time.sleep(BLINK_PERIOD*BLINK_DUTY/1000.0)
 
            # Set GPIO value to LO
            valueFile.write(GPIO_VAL_LO)
            valueFile.flush()
 
            # Sleep for blink off duration
            time.sleep(BLINK_PERIOD*(1.0-BLINK_DUTY)/1000.0)
 
    except Exception:
        print(traceback.format_exc())
 
    return
 
if __name__ == "__main__":
    main()

RTC - Read Time

Note: The modules fcntl and glob are provided by the packages 'python-fcntl' and 'python-shell' respectively.

#!/usr/bin/env python
 
import fcntl, struct, glob
 
RTC_RD_TIME=0x80247009
 
# Identify RTCs
rtcList = glob.glob('/dev/rtc[0-9]')
print "RTCs: ", rtcList, "\n"
 
# Read each RTC
for rtc in rtcList
    #  struct rtc_time {
    #    int tm_sec;
    #    int tm_min;
    #    int tm_hour;
    #    int tm_mday;
    #    int tm_mon;
    #    int tm_year;
    #    int tm_wday;
    #    int tm_yday;
    #     int tm_isdst;
    #  };
    a=struct.pack('iiiiiiiii', 0,0,0,0,0,0,0,0,0)
 
    fo=open(rtc)
 
    input=fcntl.ioctl(fo.fileno(), RTC_RD_TIME, a)
    result=struct.unpack('iiiiiiiii', input)
 
    print rtc + ": " + str(1900+result[5]) + "-" + str(1+result[4]) + "-" + str(result[3]) + " " + \
          str(result[2]) + ":" + str(result[1]) + ":" + str(result[0]) + " UTC"