Python

How to fix Python ModuleNotFoundError: No module named ‘usb’ / usb.util

Problem:

When running a Python script, you see an error message like

Traceback (most recent call last):
  File "./dfuse-tool.py", line 2, in <module>
    import dfuse
  File "/home/uli/dev/tools/dfuse-tool/dfuse/__init__.py", line 1, in <module>
    from dfuse.DfuDevice import DfuDevice
  File "/home/uli/dev/tools/dfuse-tool/dfuse/DfuDevice.py", line 1, in <module>
    import usb.util
ModuleNotFoundError: No module named 'usb'

Solution:

You need to install PyUSB using

sudo pip3 install pyusb
Posted by Uli Köhler in Electronics, Python

How to make video from Matplotlib plots

First, you need to ensure that your plots are saved all with the same name pattern containing the frame number (starting at 1) which must be padded with zeros! For example, your plot should be named myplot_000001.png or myplot_0123. This can be done using, for example, using

fig.savefig(f'myplots/myplot_{timestep:06d}.png')

06d pads the number (in the timestep variable) up to 6 digits in total.

Now, use ffmpeg like this to create the video:

ffmpeg -f image2 -framerate 25 -i myplots/myplot_%06d.png -vcodec libx264 -crf 22 video.mp4

 

Posted by Uli Köhler in Data science, Python

How to autoscale matplotlib X&Y axis after set_data() call

After calling set_data() or set_ydata() in matplotlib the axis scale is not updated automatically. Use

ax.relim()
ax.autoscale_view(True,True,True)

to update both the X and the Y scale.

Full example:

line.set_data(x, y)
# Autoscale view
ax.relim()
ax.autoscale_view(True,True,True)
# Redraw
figure.canvas.draw()
figure.canvas.flush_events()

 

Posted by Uli Köhler in Python

How to fix pip cartopy error: “geos_c.h: No such file or directory”

Problem:

When trying to install cartopy on Linux using e.g. sudo pip3 install cartopy, you see an error message like

lib/cartopy/trace.cpp:633:10: fatal error: geos_c.h: Datei oder Verzeichnis nicht gefunden                                                                                                 
  633 | #include "geos_c.h"                                                                                                                                                                
      |          ^~~~~~~~~~                                                                                                                                                                
compilation terminated.                                                                                                                                                                    
setup.py:117: UserWarning: Unable to determine GEOS version. Ensure you have 3.3.3 or later installed, or installation may fail.

Solution:

Install libgeos-dev using

sudo apt -y install libgeos-dev

 

Posted by Uli Köhler in Cartopy, Python

How I fixed Python OSError: [Errno 99] Cannot assign requested address

Problem:

When binding a socket, you see an error message like

Traceback (most recent call last):
  File "run.py", line 91, in <module>
    server = start_server(loop, ('192.168.1.100', 8080))
  File "run.py", line 86, in start_server
    transport, server = loop.run_until_complete(t)
  File "/usr/lib/python3.8/asyncio/base_events.py", line 616, in run_until_complete
    return future.result()
  File "/usr/lib/python3.8/asyncio/base_events.py", line 1332, in create_datagram_endpoint
    raise exceptions[0]
  File "/usr/lib/python3.8/asyncio/base_events.py", line 1316, in create_datagram_endpoint
    sock.bind(local_address)
OSError: [Errno 99] Cannot assign requested address

Solution:

In my case, the issue was that I was trying to bind the specific IP address 192.168.1.100 but the computer running the script did not have said IP address configured on any interface.

server = start_server(loop, ('192.168.1.100', 8080))

so I needed to change the bind IP address to either 0.0.0.0 to listen to ANY IP address or I needed to change 192.168.1.100 to the IP address of the host computer I am running the script on.

Docker container [Errno 99] Cannot assign requested address

Note that for Docker containers, either you need to run them in network_mode: host to use the host’s network systemd, or you need to bind to the container’s IP address. You can not bind to the host’s IP address from the contaienr unless using network_mode: host! But you can forward the ports from the host, binding to a specific IP address.

Posted by Uli Köhler in Python

Python raw TCP client minimal example & cheatsheet

import socket

try:
    # Initialize socket
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.connect(("192.168.238.1", 12345))
    
    # Example: Send
    sock.send(b"abc123abc")

    # Example: Receive with timeout
    sock.settimeout(0.1)
    data = sock.recv(2048)
finally:
    sock.close()

 

Posted by Uli Köhler in Networking, Python

How to set log level for structlog

You can set the structlog log level by using the logging module:

import logging

# Example 1: Output only DEBUG and higher levels
structlog.configure(
    wrapper_class=structlog.make_filtering_bound_logger(logging.DEBUG),
)

# Example 2: Output only INFO and higher levels
structlog.configure(
    wrapper_class=structlog.make_filtering_bound_logger(logging.INFO),
)

 

Posted by Uli Köhler in Python

How to DNS query specific nameservers in Python

In networking, you sometimes need to resolve a hostname using a specific nameserver, be it for testing purposes or because some hostnames are only resolveable internally.

This can be done using dnspython which you can install using pip3 install dnspython.

The following example illustrates the easiest way of performing a query.

import dns.resolver

def dns_query_specific_nameserver(query="techoverflow.net", nameserver="1.1.1.1", qtype="A"):
    """
    Query a specific nameserver for:
    - An IPv4 address for a given hostname (qtype="A")
    - An IPv6 address for a given hostname (qtype="AAAA")
    
    Returns the IP address as a string
    """
    resolver = dns.resolver.Resolver(configure=False)
    resolver.nameservers = [nameserver]
    answer = resolver.resolve(query, qtype)

    if len(answer) == 0:
        return None
    else:
        return str(answer[0])
    
# NOTE: May throw dns.resolver.NXDOMAIN, dns.resolver.NoAnswer or similar

# IPv4 Usage example
dns_query_specific_nameserver(qtype="A") # e.g. '172.67.166.211'
# IPv6 usage example
dns_query_specific_nameserver(qtype="AAAA") # e.g. '2606:4700:3035::ac43:a6d3'

 

 

Posted by Uli Köhler in Networking, Python

How to get external IPv6 address in Python

Also see: How to get external IPv4 address in Python

You can use the free IPify service together with requests to get your current external IPv6 address. Note that this will only work if IPv6 is enabled on the host running the script, and it has a valid IPv6 configuration. Most notably, on Docker containers it will typically only work in network_mode: host:

#!/usr/bin/env python3
import requests

def get_current_ipv6():
    """Get the current external IPv6 address or return None if no connection to the IPify service is possible"""
    try:
        return requests.get("https://api6.ipify.org", timeout=5).text
    except requests.exceptions.ConnectionError as ex:
        return None

# Usage example
print(get_current_ipv6()) # Prints e.g. 2a01:4f9:c010:278::1
Posted by Uli Köhler in Networking, Python

How to get external IPv4 address in Python

Also see: How to get external IPv6 address in Python

You can use the free IPify service together with requests to get your current IPv4 address:

#!/usr/bin/env python3
import requests

def get_current_ipv4():
    """Get the current external IPv4 address or return None if no connection to the IPify service is possible"""
    try:
        return requests.get("https://api4.ipify.org", timeout=5).text
    except requests.exceptions.ConnectionError as ex:
        return None

# Usage example
print(get_current_ipv4()) # Prints e.g. 95.216.138.188

 

Posted by Uli Köhler in Networking, Python

How to fix elasticsearch.exceptions.RequestError: RequestError(400, ‘resource_already_exists_exception’, ‘index […] already exists’) in Python

Problem:

You want to create an ElasticSearch index in Python using code like

es.indices.create("nodes") # Create an index names "nodes"

but you see the following error message:

Traceback (most recent call last):
  File "estest.py", line 22, in <module>
    es.indices.create("nodes")
  File "/usr/local/lib/python3.8/dist-packages/elasticsearch/client/utils.py", line 168, in _wrapped
    return func(*args, params=params, headers=headers, **kwargs)
  File "/usr/local/lib/python3.8/dist-packages/elasticsearch/client/indices.py", line 123, in create
    return self.transport.perform_request(
  File "/usr/local/lib/python3.8/dist-packages/elasticsearch/transport.py", line 415, in perform_request
    raise e
  File "/usr/local/lib/python3.8/dist-packages/elasticsearch/transport.py", line 381, in perform_request
    status, headers_response, data = connection.perform_request(
  File "/usr/local/lib/python3.8/dist-packages/elasticsearch/connection/http_urllib3.py", line 277, in perform_request
    self._raise_error(response.status, raw_data)
  File "/usr/local/lib/python3.8/dist-packages/elasticsearch/connection/base.py", line 330, in _raise_error
    raise HTTP_EXCEPTIONS.get(status_code, TransportError)(
elasticsearch.exceptions.RequestError: RequestError(400, 'resource_already_exists_exception', 'index [nodes/mXAiBt0wTKK4Y31HpshVbw] already exists')

Solution:

The error message tells you that the index you are trying to create already exists!

The simples solution is to use the code from our post on How to create ElasticSearch index if it doesn’t already exist in Python:

def es_create_index_if_not_exists(es, index):
    """Create the given ElasticSearch index and ignore error if it already exists"""
    try:
        es.indices.create(index)
    except elasticsearch.exceptions.RequestError as ex:
        if ex.error == 'resource_already_exists_exception':
            pass # Index already exists. Ignore.
        else: # Other exception - raise it
            raise ex

and use that function to create your index:

es_create_index_if_not_exists(es, "nodes") # Creates the "nodes" index ; doesn't fail if it already exists

 

Posted by Uli Köhler in Databases, ElasticSearch, Python

How to create ElasticSearch index if it doesn’t already exist in Python

The following utility will create an index if it doesn’t exist already by ignoring any resource_already_exists_exception

def es_create_index_if_not_exists(es, index):
    """Create the given ElasticSearch index and ignore error if it already exists"""
    try:
        es.indices.create(index)
    except elasticsearch.exceptions.RequestError as ex:
        if ex.error == 'resource_already_exists_exception':
            pass # Index already exists. Ignore.
        else: # Other exception - raise it
            raise ex

# Example usage: Create "nodes" index
es_create_index_if_not_exists(es, "nodes")

 

Posted by Uli Köhler in Databases, ElasticSearch, Python

How to fix Elasticsearch Python elasticsearch.exceptions.NotFoundError: NotFoundError(404, ‘index_not_found_exception’, ‘no such index [node]’, node, index_or_alias)

Problem:

You want to update ElasticSearch index settings in Python using code like

es.indices.put_settings(index="node", body={
    "index.mapping.total_fields.limit": 100000
})

but you see an error message like this one:

Traceback (most recent call last):
  File "estest.py", line 11, in <module>
    es.indices.put_settings(index="node", body={
  File "/usr/local/lib/python3.8/dist-packages/elasticsearch/client/utils.py", line 168, in _wrapped
    return func(*args, params=params, headers=headers, **kwargs)
  File "/usr/local/lib/python3.8/dist-packages/elasticsearch/client/indices.py", line 786, in put_settings
    return self.transport.perform_request(
  File "/usr/local/lib/python3.8/dist-packages/elasticsearch/transport.py", line 415, in perform_request
    raise e
  File "/usr/local/lib/python3.8/dist-packages/elasticsearch/transport.py", line 381, in perform_request
    status, headers_response, data = connection.perform_request(
  File "/usr/local/lib/python3.8/dist-packages/elasticsearch/connection/http_urllib3.py", line 277, in perform_request
    self._raise_error(response.status, raw_data)
  File "/usr/local/lib/python3.8/dist-packages/elasticsearch/connection/base.py", line 330, in _raise_error
    raise HTTP_EXCEPTIONS.get(status_code, TransportError)(
elasticsearch.exceptions.NotFoundError: NotFoundError(404, 'index_not_found_exception', 'no such index [node]', node, index_or_alias)

Solution:

The index needs to be created first in order to be able to put settings. First, double-check if you spelled the index name correctly! You can see the index name in the exception: no such index [node] means that the index is called node

The direct way to create an index is

es.indices.create("node")

But note that this will fail if the index already exists. In order to work around this issue, I recommend to use the code from our previous post How to create ElasticSearch index if it doesn’t already exist in Python:

def es_create_index_if_not_exists(es, index):
    """Create the given ElasticSearch index and ignore error if it already exists"""
    try:
        es.indices.create(index)
    except elasticsearch.exceptions.RequestError as ex:
        if ex.error == 'resource_already_exists_exception':
            pass # Index already exists. Ignore.
        else: # Other exception - raise it
            raise exnodes

and use the es_create_index_if_not_exists()  function to create your index:

es_create_index_if_not_exists(es, "node") # Creates the "node" index ; doesn't fail if it already exists

 

Posted by Uli Köhler in Databases, ElasticSearch, Python

How to set index setting using Python ElasticSearch client

You can set index settings using the official ElasticSearch python client library by using:

es.indices.put_settings(index="my-index", body={
    # Put your index settings here
    # Example: "index.mapping.total_fields.limit": 100000
})

Full example:

from elasticsearch import Elasticsearch

es = Elasticsearch()

es.indices.put_settings(index="ways", body={
    "index.mapping.total_fields.limit": 100000
})
Posted by Uli Köhler in Databases, ElasticSearch, Python

How to get IndicesClient when using ElasticSearch Python API

When using the official ElasticSearch Python client, you can get the IndicesClient for an Elasticsearch instance by using

es.indices

Full example:

from elasticsearch import Elasticsearch

es = Elasticsearch()

indices_client = es.indices

 

Posted by Uli Köhler in Databases, ElasticSearch, Python

How to increase ElasticSearch total field limit using Python API

When using ElasticSearch, you will sometimes encounter an Limit of total fields [1000] has been exceeded when you insert a large document.

One solution that often works for real-world scenarios is to just increase the default limit of 1000 to, for example, 100000 to account for even the largest documents.

How this can be done is, however, not well-documented for the ElasticSearch Python API. Here’s how you can do it:

from elasticsearch import Elasticsearch

es = Elasticsearch()

es.indices.put_settings(index="my-index", body={
    "index.mapping.total_fields.limit": 100000
})

 

Posted by Uli Köhler in Databases, ElasticSearch, Python

Faster Python Elasticsearch index() by using concurrent.futures ThreadPoolExecutor

In our previous post Elasticsearch Python minimal index() / insert example we showed how to insert a document into Elasticsearch.

When inserting a large number of documents into Elasticsearch, you will notice that it’s extremely slow to wait for the API call to finish before trying to insert the document.

In this post we’ll show a simple way of doing many requests in parallel so multiple index operations are running concurrently while your code is processing more documents. For this, we’ll use concurrent.futures.ThreadPoolExecutor and – after inserting all documents into the queue, use concurrent.futures.wait to wait for all requests to finish before we’ll exit.

#!/usr/bin/env python3
from elasticsearch import Elasticsearch
from concurrent.futures import ThreadPoolExecutor
import concurrent.futures

index_executor = ThreadPoolExecutor(64)
futures = []

es = Elasticsearch()
for i in range(1000):
    future = index_executor.submit(es.index, index="test-index", id=i, body={"test": 123})
    futures.append(future)

print("Waiting for requests to complete...")
concurrent.futures.wait(futures)

 

Posted by Uli Köhler in Databases, ElasticSearch, Python

Elasticsearch Python minimal index() / insert example

This minimal example inserts a single document into Elasticsearch running at http://localhost:9200:

#!/usr/bin/env python3
from elasticsearch import Elasticsearch

es = Elasticsearch()
es.index(index="test-index", id=1, body={"test": 123})

 

Posted by Uli Köhler in Databases, ElasticSearch, Python

How to use Cloudflare API key instead of token in Python Cloudflare API

Problem:

You want to access Cloudflare using the Cloudflare Python API like this:

#!/usr/bin/env python3
import CloudFlare
cf = CloudFlare.CloudFlare(
    email="me@mydomain.com",
    token="Oochee3_aucho0aiTahc8caVuak6Que_N_Aegi9o"
)
# ...

but when you try to use the key=... argument like this:

cf = CloudFlare.CloudFlare(
    email="me@mydomain.com",
    key="Oochee3_aucho0aiTahc8caVuak6Que_N_Aegi9o"
)

you see this error message:

Traceback (most recent call last):
  File "run.py", line 4, in <module>
    cf = CloudFlare.CloudFlare(
TypeError: __init__() got an unexpected keyword argument 'key'

Solution:

Just use the key in the token=... argument like this:

cf = CloudFlare.CloudFlare(
    email="me@mydomain.com",
    token="[YOUR API KEY]"
)

This usage is officially documented in the README section of the Cloudflare API.

Posted by Uli Köhler in Networking, Python