Python

Inventree Python API: How to list all part categories

See our previous post Inventree Python API: Minimal API connect example using YAML config for our method of creating the api object using a YAML config file!

from inventree.part import PartCategory

all_categories = PartCategory.list(api)

# Dict of part categories by name
# (e.g. 'OpAmps')
part_categories_by_name = {
    category["name"]: category
    for category in all_categories
}
# Dict of part categories by public key (e.g. 7)
part_categories_by_pk = {
    category.pk: category
    for category in all_categories
}
# Dict of part categories by hierarchical path
# (e.g. 'Electronics-Components/ICs/OpAmps')
part_categories_by_pathstring = {
    category.pathstring: category
    for category in all_categories
}

Note that this code by itself does not take into account the hierarchy of the categories.

Example output

part_categories_by_name:

{'Elektronik-Komponenten': <inventree.part.PartCategory at 0x7fd735125510>,
 'ICs': <inventree.part.PartCategory at 0x7fd7356d1720>,
 'OpAmps': <inventree.part.PartCategory at 0x7fd735ead4e0>}

part_categories_by_pk:

{1: <inventree.part.PartCategory at 0x7fd7346b5db0>,
 2: <inventree.part.PartCategory at 0x7fd73438aa10>,
 3: <inventree.part.PartCategory at 0x7fd727325b40>}

part_categories_by_pathstring:

{'Elektronik-Komponenten': <inventree.part.PartCategory at 0x7fd727bd3040>,
 'Elektronik-Komponenten/ICs': <inventree.part.PartCategory at 0x7fd727bd1db0>,
 'Elektronik-Komponenten/ICs/OpAmps': <inventree.part.PartCategory at 0x7fd727bd1060>}

 

Posted by Uli Köhler in InvenTree, Python

Inventree Python API: Minimal API connect example using YAML config

This example allows you to create a config file inventree.yml:

server: 'https://inventree.mydomain.com'
username: 'admin'
password: 'Chuiquu5Oqu8il5ahLoja4aecai4ee'

and then read it in Python and connect to the Inventree API:

from inventree.api import InvenTreeAPI

# Load config
import yaml

with open("inventree.yml", "r") as file:
    config = yaml.safe_load(file)
    
api = InvenTreeAPI(config["server"], username=config["username"], password=config["password"])

 

Posted by Uli Köhler in InvenTree, Python

PySerial minimal RFC2217 example: Copy data received from serial port to stdout

Also see the same example for a local serial port: PySerial minimal example: Copy data received from serial port to stdout

This example connects to the RFC2217 remote serial port on 10.1.2.3 port 1234. It does not send data to the serial port but only copies data received from the serial port to stdout.

#!/usr/bin/env python3
import serial
with serial.serial_for_url("rfc2217://10.1.2.3:1234", baudrate=115200) as ser:
    try:
        while True:
            response = ser.read()
            if response:
                print(response.decode("iso-8859-1"), end="")
    finally:
        ser.close()

 

By using iso-8859-1   decoding, we ensure that even binary bytes are decoded in some way and do not cause an exception.

Posted by Uli Köhler in Embedded, Python

How to download forum pages to HTML using Python & requests

This will download page 1000 … page 1 in descending order and save the HTML to 1000.html up to 1.html

import requests
for i in range(1000, 0, -1):
    with open(f"{i}.html", "w") as outfile:
        outfile.write(requests.get(f"https://forum.my-domain.com/showthread.php?123456-my-thread/page{i}").text)

With progress bar

import requests
from tqdm import tqdm
for i in tqdm(range(1000, 0, -1)):
    with open(f"{i}.html", "w") as outfile:
        outfile.write(requests.get(f"https://forum.my-domain.com/showthread.php?123456-my-thread/page{i}").text)

 

Posted by Uli Köhler in Python

How to setup & use WordPress REST API authentication using Python

WordPress authentication plugin setup

First, install the WordPress REST API Authentication wordpress plugin, which you can find by searching for WordPress REST API Authentication:

Then you need to open the plugin configuration page. Open Plugins in the WordPress admin panel, locate the  WordPress REST API Authentication plugin and click Configure

Select Basic Authentication:

Then click Next on the top right:

and click Finish on the next page:

Setup in Python

Assuming you have a WordPress user admin with password abc123 we can modify our code from How to get WordPress posts as JSON using Python & the WordPress REST API in order to query a non-public endpoint:

import requests
import base64

# Compute basic authentication header
auth_header = b"Basic " + base64.b64encode(b"admin:abc123")

# posts is a list of JSON objects, each representing a post
posts = requests.get("https://mydomain.com/wp-json/wp/v2/posts",
                     params={"context": "edit"},
                     headers={"Authorization": auth_header}).json()

 

Posted by Uli Köhler in Python, Wordpress

How to get all WordPress posts as JSON using Python & the WordPress REST API

In our previous post How to get WordPress posts as JSON using Python & the WordPress REST API we showed how to fetch a single page of 10 posts using the WordPress REST API in Python.

In this post, we’ll use the pagination in order to fetch a list of all the posts.

Firstly, we observe that once we query an invalid page such as ?page=1000000, the returned JSON will be

{'code': 'rest_post_invalid_page_number',
 'message': 'The page number requested is larger than the number of pages available.',
 'data': {'status': 400}}

instead of the JSON array representing the list of posts.

Using this information, we can write a fetcher that fetches pages of 100 posts each until this error message is encountered:

from tqdm import tqdm
import requests

def page_numbers():
    """Infinite generate of page numbers"""
    num = 1
    while True:
        yield num
        num += 1

posts = []
for page in tqdm(page_numbers()):
    # Fetch the next [pagesize=10] posts
    posts_page = requests.get("https://mydomain.com/wp-json/wp/v2/posts", params={"page": page, "per_page": 100}).json()
    # Check for "last page" error code
    if isinstance(posts_page, dict) and posts_page["code"] == "rest_post_invalid_page_number": # Found last page
        break
    # No error code -> add posts
    posts += posts_page

 

 

Posted by Uli Köhler in Python, Wordpress

How to get WordPress posts as JSON using Python & the WordPress REST API

You can use the requests library to fetch the posts as JSON from /wp-json/wp/v2/posts

On the wordpress site, you typically don’t need to configure this – the JSON API is enabled by default and accessing publically available info can be done without authentication.

import requests

# posts is a list of JSON objects, each representing a post
posts = requests.get("https://mydomain.com/wp-json/wp/v2/posts").json()

This will, by default, fetch the most recent 10 posts. See TODO for more info on how to fetch all posts using pagination

Posted by Uli Köhler in Python, Wordpress

Netmiko MikroTik RouterOS minimal example

This example prints the identity (i.e. user-defined name) of the switch/router at IP address 10.0.0.1 with password abc123abc.

from netmiko import ConnectHandler
mikrotik = {
    'device_type': 'mikrotik_routeros',
    'host':   '10.0.0.1',
    'username': 'admin',
    'password': 'abc123abc'
}

mikrotik_connection = ConnectHandler(**mikrotik)
print(mikrotik_connection.send_command(f'/system/identity/print', cmd_verify=False))

Example output:

name: MySwitch01

 

Posted by Uli Köhler in MikroTik, Networking, Python

How to find first element in sorted numpy array that is larger than a given scalar

Also see How to find first element in sorted numpy array that is smaller than a given scalar

You can use np.searchsorted() like this:

idx = np.searchsorted(arr, scalar, side='right') + 1

Full example:

import numpy as np

# Example array
arr = np.array([1, 2, 3, 4, 5])

# We'll search for this value
scalar = 3.5

# Use numpy.searchsorted() to find the first element in the array that is larger than the scalar
# We need to use + 1 since searchsorted() returns the first element that is smaller than the scalar
idx = np.searchsorted(arr, scalar, side='right') + 1

# Print the resulting index
print(idx)

 

Posted by Uli Köhler in Python

How to find first element in sorted numpy array that is smaller than a given scalar

Also see How to find first element in sorted numpy array that is larger than a given scalar

You can use np.searchsorted() like this:

idx = np.searchsorted(arr, scalar, side='left')

Full example:

import numpy as np

# Example array
arr = np.array([1, 2, 3, 4, 5])

# We'll search for this value
scalar = 3.5

# Use numpy.searchsorted() to find the first element in the array that is smaller than the scalar
idx = np.searchsorted(arr, scalar, side='left')

# Print the resulting index
print(idx)

 

Posted by Uli Köhler in Python

How to add progress bar to “for i in range(…)” loop in Python

You can use the tqdm library to easily add a progress bar to your Python for i in range(...) loop:

Just

from tqdm import tqdm

at the top of your script and then surround range(...) with tqdm(...). For example,

mysum = 0
for i in range(50):
    sum += i

becomes

mysum = 0
for i in tqdm(range(50)):
    mysum += i

 

Posted by Uli Köhler in Python

How to create NumPy array that is exactly as long as another array

Want to have the same size as a but a different data type? See How to create NumPy array that is exactly as long as another array but has a different data type

If you have a numpy array such as

a = np.arange(50)

you can use the following code to create an array b that has the same dimensions, i.e. the same size, and the same datataype as a using

b = np.zeros_like(a)

 

 

Posted by Uli Köhler in Python

How to create NumPy array that is exactly as long as another array but has a different data type

Want to have the same data type as a as well as the same size? See How to create NumPy array that is exactly as long as another array

If you have a numpy array such as

a = np.arange(50) * 1.1

you can use the following code to create an array b that has the same dimensions, i.e. the same size as a, but uses the np.intinteger data type as opposed to the floating point data type of a:

b = np.zeros_like(a, dtype=np.int)

 

 

Posted by Uli Köhler in Python

How to check if a NumPy array is a 1D array

Use .ndim == 1 which contains the number of dimensions in the array.

if a.ndim == 1:
    print('a is a 1D array')
else:
    print('a is not a 1D array')

You can also use assert to check that:

assert a.ndim == 1

which will raise an AssertionError if a is not a one-dimensional array.

Posted by Uli Köhler in Python

How to enable/disable PoE ports using pySNMP on the Netgear GS710TUP

In our previous post How to use pySNMP to query SNMPv3 information from Netgear GS710TUP we showed how to connect pySNMP to the Netgear GS710TUP to query simple informaton.

The following example script is the pySNMP equivalent to How to enable/disable PoE port power using SNMPv3 on the Netgear GS710TUP : It sets the relevant OID in 1.3.6.1.2.1.105.1.1.1.3 (pethPsePortAdminEnable).

The following OIDs for individual ports are available for the GS710TUP which has 8 PoE ports:

1.3.6.1.2.1.105.1.1.1.3.1.1 # Port 1
1.3.6.1.2.1.105.1.1.1.3.1.2 # Port 2
1.3.6.1.2.1.105.1.1.1.3.1.3 # Port 3
1.3.6.1.2.1.105.1.1.1.3.1.4 # Port 4
1.3.6.1.2.1.105.1.1.1.3.1.5 # Port 5
1.3.6.1.2.1.105.1.1.1.3.1.6 # Port 6
1.3.6.1.2.1.105.1.1.1.3.1.7 # Port 7
1.3.6.1.2.1.105.1.1.1.3.1.8 # Port 8

In our example, we’ll enable the power to port 1:

import pysnmp.hlapi as snmp

portNumber = 1

iterator = snmp.setCmd(
    snmp.SnmpEngine(),
    snmp.UsmUserData('admin', 'SWITCH_ADMIN_PASSWORD',
                     authProtocol=snmp.usmHMACSHAAuthProtocol,
                     privProtocol=snmp.usmNoPrivProtocol),
    snmp.UdpTransportTarget(('SWITCH_IP_ADDRESS', 161)),
    snmp.ContextData(),
    snmp.ObjectType(
        snmp.ObjectIdentity(f'1.3.6.1.2.1.105.1.1.1.3.1.{portNumber}'),
        snmp.Integer(1))
)

errorIndication, errorStatus, errorIndex, varBinds = next(iterator)

if errorIndication:
    print(errorIndication)
elif errorStatus:
    idx = int(errorIndex) - 1
    location = errorIndex and varBinds[idx][0] or '?'
    print(f"{errorStatus.prettyPrint()} at {location}")
else: # Success
    for varBind in varBinds:
        print(' = '.join([x.prettyPrint() for x in varBind]))

In order to disable power on port 1, replace

snmp.Integer(1)

by

snmp.Integer(2)

Note that an 1 value represents boolean true (as in enable PoE output) whereas 2 represents boolean false, disabling PoE output on the port.

Posted by Uli Köhler in Networking, PoE, Python, SNMP

How to use pySNMP to query SNMPv3 information from Netgear GS710TUP

First, install pysnmp using

pip install pysnmp

On the Netgear GS710TUP, I enabled SNMPv3 without encryption/privacy but with SHA1 authentication as already outlined in our previous article Simple SNMPv3 client example for Netgear GS710TUP:

Using pysnmp, you can query the device like this (using the standard admin password which you also use to login to the router:

#!/usr/bin/env python3
import pysnmp.hlapi as snmp

iterator = snmp.getCmd(
    snmp.SnmpEngine(),
    snmp.UsmUserData('admin', 'SWITCH_ADMIN_PASSWORD',
                     authProtocol=snmp.usmHMACSHAAuthProtocol,
                     privProtocol=snmp.usmNoPrivProtocol),
    snmp.UdpTransportTarget(('SWITCH_IP_ADDRESS', 161)),
    snmp.ContextData(),
    snmp.ObjectType(snmp.ObjectIdentity('SNMPv2-MIB', 'sysDescr', 0))
)

errorIndication, errorStatus, errorIndex, varBinds = next(iterator)

if errorIndication:
    print(errorIndication)
elif errorStatus:
    idx = int(errorIndex) - 1
    location = errorIndex and varBinds[idx][0] or '?'
    print(f"{errorStatus.prettyPrint()} at {location}")
else: # Success
    for varBind in varBinds:
        print(' = '.join([x.prettyPrint() for x in varBind]))

Example output:

SNMPv2-MIB::sysDescr.0 = GS710TUP 10-Port Gigabit Ethernet Ultra60 PoE++ Smart Managed Pro Switch (480W), Software Version 1.0.5.9, Boot Version 1.0.0.9
Posted by Uli Köhler in Networking, Python, SNMP

How to check if system is Windows or Linux using Python

def is_platform_windows()
    return platform.system() == "Windows"

def is_platform_linux():
    return platform.system() == "Linux"

 

Posted by Uli Köhler in Python

How to fix Python msgpack TypeError: write() argument must be str, not bytes

Problem:

While trying to write an object to a file using msgpack using code like

with open("myobj.msgpack", "w") as outfile:
    msgpack.dump(myobj, outfile)

you see an error message like

File /usr/lib/python3/dist-packages/msgpack/__init__.py:26, in pack(o, stream, **kwargs)
     20 """
     21 Pack object `o` and write it to `stream`
     22 
     23 See :class:`Packer` for options.
     24 """
     25 packer = Packer(**kwargs)
---> 26 stream.write(packer.pack(o))

TypeError: write() argument must be str, not bytes

Solution:

msgpack writes binary data, so you need to open the file in binary mode using open(..., "wb"):

with open("myobj.msgpack", "wb") as outfile:
    msgpack.dump(myobj, outfile)

 

Posted by Uli Köhler in Python

How to reload “from mymodule import *” wildcard imports in Jupyter

In our previous post How to reload import in Jupyter we showed how to use reload() from importlib in order to reload a module without restarting the kernel.

import mymodule
# Reload .py file every time we run the cell
from importlib import reload
reload(mymodule)

However, often we are using syntax like

from mymodule import *

to wildcard-load everything imported from mymodule.

We can reload those wildcard imports using the same strategy by additionalling importing mymodule and then reloading using reload(mymodule) – this will also reload the wildcard import from mymodule import *!

from mymodule import *

# This line is just to facilitate reloading
import mymodule
from importlib import reload
reload(mymodule)
# Reload .py file every time we run the cell from importlib import reload reload(mymodule)

 

Posted by Uli Köhler in Python

How to add N months to date using arrow in Python

If you have a date like

import arrow

dt = arrow.get("2021-05-27")

you can add N months (example: 18 months) to said date using

dt.shift(months=+18)

The result will be an arrow representation of 2022-11-27

Posted by Uli Köhler in Python