Multiple Python versions on Windows

Hi πŸ‘‹

In this short article I will show you two ways of changing Python versions on Windows. It is useful when you have installed multiple Python versions on your system and want to run a specific version from the terminal.

For example, if we have the following versions installed:

We can use either the Python Launcher py to run Python or the python command.

Python Launcher

To list installed Python versions with Python launcher we can use the py -0 command.

@nutiu ➜ ~ py -0
Installed Pythons found by C:\WINDOWS\py.exe Launcher for Windows
 -3.10-64 *
 -3.7-64

@nutiu ➜ ~ py
Python 3.10.3 (tags/v3.10.3:a342a49, Mar 16 2022, 13:07:40) [MSC v.1929 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>>

The default version has a star next to it. If we run a simple py command, we’ll get a prompt to Python 3.10. To change the default version all we need to do is to set the environment variable PY_PYTHON to the desired version.

@nutiu ➜ ~ $env:PY_PYTHON = "3.7"
@nutiu ➜ ~ py -0
Installed Pythons found by C:\WINDOWS\py.exe Launcher for Windows
 -3.10-64
 -3.7-64 *
@nutiu ➜ ~ py
Python 3.7.0 (v3.7.0:1bf9cc5093, Jun 27 2018, 04:59:51) [MSC v.1914 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>>

Using the Python command

If you prefer running Python using the full command then you’ll get the Python version which has higher precedence in your path, for example if I run python on my machine I will get:

@nutiu ➜ ~ python
Python 3.10.3 (tags/v3.10.3:a342a49, Mar 16 2022, 13:07:40) [MSC v.1929 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>>

We can change the order by going to: My PC -> Advanced System Settings -> Environment Variables

Select path from User variables and click Edit…

Python 3.10 has higher precedence in path because it is above Python 3.7. If we want to change the order, we need to select the folders referencing Python37 and click Move Up until they are above Python 3.10

Restarting your terminal and running python again should run your desired Python version.

Thanks for reading! 🍻

A custom HomeKit accessory with Python

Hi πŸ‘‹,

In this short article I want to showcase how I implemented a custom HomeKit accessory with python.

My Home Assistant’s SD card died πŸͺ¦ a few days ago and the support for GPIO based sensors will be removed in newer releases. This makes it unsuitable for my needs, while giving me the perfect opportunity to try other things.

To continue monitoring temperature and humidity in my home I’ve built a custom HomeKit accessory with HAP Python.

The Sensor

A BME680 air quality sensor is used to monitor temperature and humidity. It is connected to the PI according to the following diagram:

The communication with the Pi is done using the I2C protocol. If you want to use I2C in your own setup, it has to be enabled using raspi-config, as it doesn’t come enabled by default.

# Execute
sudo raspi-config
# Then select Interfacing options->I2C and enable it.

Connection can be tested with the following command:

sudo apt-get install build-essential libi2c-dev i2c-tools python-dev libffi-dev git
/usr/sbin/i2cdetect -y 1
pi@raspberrypi:~ $ i2cdetect -y 1
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:                         -- -- -- -- -- -- -- -- 
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
70: -- -- -- -- -- -- 76 -- 

It will output the address that the sensor is using, in our case the 0x76 I2C address.

The Code for the Accessory

You can browse the full code for the accessory and bme680 sensor in my git repo.

To run the program, clone the repository and ensure that you’re running it under the pi user, otherwise you will need to change some things.

cd /home/pi && git clone git@github.com:dnutiu/bme680-homekit.git && cd bme680-homekit
sudo apt-get install libavahi-compat-libdnssd-dev
pip3 install -r requirements.txt

Verify that the program works by running python3 main.py. Running it the first time will prompt you to add the accessory to the Home app. If you miss this step you can repeat it by deleting the accessory.state file located in pi’s home directory and by running the program again.

After you’ve verified that it works, you can setup a systemd service to run the accessory’s python script when the PI boots

Copy the bme680-homekit.service to /etc/systemd/system and check that the service is running.

sudo cp bme680-homekit.service /etc/systemd/system
sudo systemctl status bme680-homekit

If you want to run this under another user rather than the pi, you’ll need to tweak the bme680-homekit.service file.

Congratulations for making it this far! πŸŽ‰

You can browse more code examples in the HAP-Python repository.

Thanks for reading and have fun! πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’» βš™οΈ

How to install aΒ specific Python version on Linux

Hello, πŸ‘‹

In this article I will show you how to install Python versions on Linux using the following methods: compiling from source, dead snakes ppa and pyenv.

To make things easier, if you want to follow along in an environment that you can break, you can create a local Kubernetes cluster using Minikube.

Next, I’m going to use the following yaml file to create an Ubuntu pod:

apiVersion: v1
kind: Pod
metadata:
  name: ubuntu
  labels:
    app: ubuntu
spec:
  containers:
  - image: ubuntu
    command:
      - "sleep"
      - "604800"
    imagePullPolicy: IfNotPresent
    name: ubuntu
  restartPolicy: Always

Save the above yaml in a file ubuntu_pod.yaml and run:

kubectl apply -f ./ubuntu_pod.yaml

To get a shell on the Ubuntu pod, run:

kubectl exec -it ubuntu -- /bin/bash

To start from scratch, simply delete the pod with kubectl delete pod/ubuntu and then recreate it.

Compiling Python from source

Before compiling Python, you will need to setup the build environment, thankfully, it is straightforward.

Pyenv has great instructions on it: https://github.com/pyenv/pyenv/wiki#suggested-build-environment.

On Ubuntu, to build Python, install the following packages:

apt-get update; apt-get install make build-essential libssl-dev zlib1g-dev \
libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm \
libncursesw5-dev xz-utils tk-dev libxml2-dev libxmlsec1-dev libffi-dev liblzma-dev

Then, search the desired python version here and, for example to install Python 3.9, run:

wget https://www.python.org/ftp/python/3.9.9/Python-3.9.9.tgz
tar -xzf Python-3.9.9.tgz
cd Python-3.9.9

Then, run configure:

./configure --enable-optimizations

And finally run make install if you want to replace the default Python installation or make altinstall to install python under the binary name of python3.9

make altinstall

To test the installation run:

python3.9 --version
Python 3.9.9

pip3.9 --version
pip 21.2.4 from /usr/local/lib/python3.9/site-packages/pip (python 3.9)

Installing Python via a third party PPA deadsnakes

To install Python using the deadsnakes ppa run:

apt-get update
apt-get install software-properties-common
add-apt-repository ppa:deadsnakes/ppa
apt-get update
apt install python3.9 python3-pip

Then, to test the installation run:

root@ubuntu:/# python3.9 --version
Python 3.9.10

root@ubuntu:/# python3.9 -m pip --version
pip 20.0.2 from /usr/lib/python3/dist-packages/pip (python 3.9)

Installing Python via Pyenv

I already written an article on how to install Python using Pyenv, check it out if you wish.

https://nuculabs.wordpress.com//2020/06/27/pyenv-for-linux-users/

Thanks for reading! πŸ“š

Pytest Fixtures and Yield

Hi πŸ‘‹

In this short article I want to explain the use of the yield keyword in pytest fixtures.

What is pytest?

Pytest is a complex python framework used for writing tests. It has lots of advanced features and it supports plugins. Many projects prefer pytest in addition to Python’s unitttest library.

What is a fixture?

A test fixture is a piece of code that fixes some common functionality that is required for writing the unit tests. This functionality can be

  • a connection to the database
  • a testing http server or client
  • creation of a complex object

You can read more about test fixtures on Wikipedia.

What does yield keyword do?

In Python, the yield keyword is used for writing generator functions, in pytest, the yield can be used to finalize (clean-up) after the fixture code is executed. Pytest’s documentation states the following.

β€œYield” fixtures yield instead of return. With these fixtures, we can run some code and pass an object back to the requesting fixture/test, just like with the other fixtures.

https://docs.pytest.org/en/6.2.x/fixture.html

An example code could be the following:

@pytest.fixture()
def my_object_fixture():
    print("1. fixture code.")
    yield MyObjectThatRequiresCleanUp()
    print("4. fixture code after yield.")

Running a sample test which utilizes the fixture will output:

collected 1 item                                                                                                                                                                       

tests\test_my_object.py 1. fixture code.
2. Initializing MyObjectThatRequiresCleanUp
3. test code.
.4. fixture code after yield.

Running the same test but now with the fixture my_object_fixture2, will output:

tests\test_my_object.py 1. fixture code.
2. Initializing MyObjectThatRequiresCleanUp
2.1 Entering
3. test code.
.3.1 Exiting
Clean exit
4. fixture code after yield.

I hope I could successfully ilustrate with these examples the order in which the testing and fixture code is run.

To run the tests, I’ve used pytest --capture=tee-sys . in the project root. The file contents are attached to the end of this article. The --capture parameter is used to capture and print the tests stdout. Pytest will only output stdout of failed tests and since our example test always passes this parameter was required.

Conclusion

Pytest is a python testing framework that contains lots of features and scales well with large projects.

Test fixtures is a piece of code for fixing the test environment, for example a database connection or an object that requires a specific set of parameters when built. Instead of duplicating code, fixing the object’s creation into a fixture makes the tests easier to maintain and write.

yield is a python keyword and when it is used in conjunction with pytest fixtures it gives you a nice pythonic way of cleaning up the fixtures.

Thanks for reading! πŸ“š


Contents of the Pytest fixtures placed in tests/__init__.py

import pytest

from my_object import MyObjectThatRequiresCleanUp


@pytest.fixture()
def my_object_fixture():
    print("1. fixture code.")
    yield MyObjectThatRequiresCleanUp()
    print("4. fixture code after yield.")


@pytest.fixture()
def my_object_fixture2():
    print("1. fixture code.")
    with MyObjectThatRequiresCleanUp() as obj:
        yield obj
    print("4. fixture code after yield.")

Contents of my_object.py

class MyObjectThatRequiresCleanUp:
    def __init__(self):
        print("2. Initializing MyObjectThatRequiresCleanUp")

    def __enter__(self):
        print("2.1 Entering")
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        print("3.1 Exiting")
        if exc_type is None:
            print("Clean exit")
        else:
            print("Exception occurred: {}".format(exc_type))

Contents of test_my_object.py placed in tests/test_my_object.py

from tests import my_object_fixture


def test_my_object(my_object_fixture):
    print("3. test code.")