Programming languages

How to read single value from XLS(X) using pandas

pandas can be used conveniently to read a table of values from Excel. When extracting data from real-life Excel sheets, there are often metadata fields which are not structured as a table readable by pandas.

Reading the pandas docs, it is not obvious how we can extract the value of a single cell (without any associated headers) with a fixed position, for example:

In this example, we want to extract cell C3, that is we want to end up with a string of value Test value #123. Since there is no clear table structure in this excel sheet (and other cells might contain other values we are not interested in – for example, headlines or headers), we don’t want to have a pd.DataFrame but simply a string.

This is how you can do it:

 

def read_value_from_excel(filename, column="C", row=3):
    """Read a single cell value from an Excel file"""
    return pd.read_excel(filename, skiprows=row - 1, usecols=column, nrows=1, header=None, names=["Value"]).iloc[0]["Value"]

# Example usage
read_value_from_excel(“Test.xlsx”, “C”, 3) # Prints
Let’s explain the parameters we’re using as arguments to pd.read_excel():

  • Test.xlsx: The filename of the file you want to read
  • skiprows=2: Row number minus one, so the desired row is the first we read
  • usecols="C": Which columns we’re interested in – only one!
  • nrows=1: Read only a single row
  • header=None: Do not assume that the first row we read is a header row
  • names=["Value"]: Set the name for the single column to Value
  • .iloc[0]: From the resulting pd.DataFrame, get the first row ([0]) by index (iloc)
  • ["Value"] From the resulting row, extract the "Value" column – which is the only column available.

In my opinion, using pandas is the best way of extracting for most real-world usecases (i.e. more focused on development speed than on execution speed) because not only does it provide automatic engine selection for .xls and .xlsx files, it’s also present on most Data Science setups anyway and provides a standardized API.

Posted by Uli Köhler in pandas, Python

How to fix pandas pd.read_excel() error XLRDError: Excel xlsx file; not supported

Problem:

When trying to read an .xlsx file using pandas pd.read_excel() you see this error message:

XLRDError: Excel xlsx file; not supported

Solution:

The xlrd library only supports .xls files, not .xlsx files. In order to make pandas able to read .xlsx files, install openpyxl:

sudo pip3 install openpyxl

After that, retry running your script (if you are running a Jupyter Notebook, be sure to restart the notebook to reload pandas!).

If the error still persists, you have two choices:

Choice 1 (preferred): Update pandas

Pandas 1.1.3 doesn’t automatically select the correct XLSX reader engine, but pandas 1.3.1 does:

sudo pip3 install --upgrade pandas

If you are running a Jupyter Notebook, be sure to restart the notebook to load the updated pandas version!

Choice 2: Explicitly set the engine in pd.read_excel()

Add engine='openpyxl' to your pd.read_excel() command, for example:

pd.read_excel('my.xlsx', engine='openpyxl')

 

Posted by Uli Köhler in pandas, Python

How to compute crystal load capacitors using Python

The UliEngineering library provides a convenient way to compute which load capacitors are suitable for a given crystal oscillator circuit.

First, install UliEngineering using

sudo pip3 install UliEngineering

Now you need to find out the following parameters:

  • The load capacitance from the crystal’s datasheet. Typical values would be 7pF or 12.5pF or 20pF
  • The pin capacitance CPin from the datasheet of the IC your crystal is connected to (e.g. your Ethernet PHY or your RTC). Typical values would be between 0.5pF to 5pF
  • Estimate the stray capacitance CStray from the board (that is the capacitance of each crystal oscillator pin to the PCB ground). You typically just take an educated guess here, so here are some values to start at:
    •  Start at 3pF
    • If you have a 4+ layer board (as opposed to a two layer board), add 2pF (since the distance to the ground plane below is much smaller on 4 layer boards
    • If you have long traces >1cm (long traces to the crystal should be avoided at all costs!), add 4pF for each cm of trace beyond 1cm for 2-layer boards or add 6pF for each cm of trace beyond 1cm for 4+ layer boards

Now we’ll plug those values into UliEngineering and compute the load capacitance:

from UliEngineering.Electronics.Crystal import load_capacitors
from UliEngineering.EngineerIO import auto_print

auto_print(load_capacitors, cload="9 pF", cpin="1 pF", cstray="5 pF")

This will print 7.00 pF

If you just want to the the value and not an auto-formatted string, use load_capacitors() directly:

capacitor = load_capacitors(cload="9 pF", cpin="1 pF", cstray="5 pF")

In this example, we’ll get capacitor == 7e-12.

Note that 7pF means that you have to add a 7pF capacitor at each side of the crystal. I recommend to use only NP0/C0G capacitors here since X5R/X7R capacitors and other high-k-dielectric ceramic capacitors not only have a high temperature coefficient but also they are not as suitable for analog applications since their capacitance will change with the voltage being applied.

Note that we used a lot of guesswork in the PCB stray capacitance estimation above, so if you need high accuracy, you need to tune your crystal oscillator to work best with your specific board. See our followup post on How to tune your crystal oscillator to get the best possible frequency accuracy for further reading.

Posted by Uli Köhler in Electronics, Python

How to add pandas pd.Timestamp

You can’t directly add Pandas pd.Timestamp instances:

t1 = pd.Timestamp('now')
t2 = pd.Timestamp('now')

t1 + t2
# TypeError: unsupported operand type(s) for +: 'Timestamp' and 'Timestamp'

But you can convert them to a numpy timestamp using their asm8 attribute, convert that timestamp to an integer, add it and convert it back:

t1 = pd.Timestamp('now')
t2 = pd.Timestamp('now')

tsum = (t1.asm8.astype(np.int64) + t2.asm8.astype(np.int64))
tsum_timestamp = pd.Timestamp(tsum.astype('<M8[ns]'))

 

Posted by Uli Köhler in pandas, Python

How to get average or mean between two pandas pd.Timestamp objects?

In order to compute the mean value between two pd.Timestamp instances, subtract them to obtain a pd.Timedelta and then add said Timedelta object to the first (smaller) timestamp:

t1 = pd.Timestamp('now')
t2 = pd.Timestamp('now')

mean_timestamp = t1 + ((t2 - t1) / 2)

 

Posted by Uli Köhler in pandas, Python

How to display BytesIO containing a PNG image in Jupyter/IPython

In IPython, you can use the Image() function to show an image:

 

from IPython.display import Image
Image(filename="img1.png")

But what if you don’t have the PNG data in a file but in a BytesIO?

Use .getvalue():

from IPython.display import Image

my_bio = ... # Insert your code here
Image(my_bio.getvalue())

 

 

Posted by Uli Köhler in Python

How to export certificates from Traefik certificate store

Traefik stores certificates as base64 encoded X.509 certificates and keys inside the certificate store.

This is a python script to export certificate from Traefik certificate store .json file:

import json
import base64

# Read Traefik ACME JSON
with open("acme.json") as acme_file:
    acme = json.load(acme_file)

# Select certificates from a specific resolver
resolver_name = "my-resolver"
certificates = acme[resolver_name]["Certificates"]

# Find the specific certificate we are looking for
certificate = [certificate for certificate in certificates if "myddomain.com" in certificate["domain"].get("sans", [])][0]

# Extract X.509 certificate data
certificate_data = base64.b64decode(certificate["certificate"])
key_data = base64.b64decode(certificate["key"])

# Export certificate and key to file
with open("certificate.pem", "wb") as certfile:
    certfile.write(certificate_data)

with open("key.pem", "wb") as keyfile:
    keyfile.write(key_data)

Note that depending on what is the primary name for your certificate, you might need to use

if "myddomain.com" == certificate["domain"]["main"]

instead of

if "myddomain.com" in certificate["domain"].get("sans", [])

 

Posted by Uli Köhler in Python, Traefik

How to filter ways by tag from .osm.pbf file using Python & Osmium

In our previous post Minimal example how to read .osm.pbf file using Python & osmium we investigated how to read a .osm.pbf file and count all nodes, ways and relations.

Today, we’ll investigate how to filter for specific ways by tag using osmium working on .osm.pbf files. In this example, we’ll filter by highwaytrunk

#!/usr/bin/env python3
import osmium as osm
from collections import namedtuple
import itertools

Way = namedtuple("Way", ["id", "nodes", "tags"])

class FindHighwayTrunks(osm.SimpleHandler):
    """
    Find ways with "highway: trunk" tag
    """

    def __init__(self):
        osm.SimpleHandler.__init__(self)
        self.ways = []

    def way(self, way):
        # If this way is a highway: trunk, ...
        if way.tags.get("highway") == "trunk":
            # ... add it to self.ways
            # NOTE: We may not keep a reference to the way object,
            # so we have to create a new Way object
            nodes = [node.ref for node in way.nodes]
            self.ways.append(Way(way.id, nodes, dict(way.tags)))

# Find ways with the given tag
way_finder = FindHighwayTrunks()
way_finder.apply_file("kenya-latest.osm.pbf")

print(f"Found {len(way_finder.ways)} ways")

This example uses kenya-latest.osm.pbf downloaded from Geofabrik, but you can use any .osm.pbf file. Parsing said file takes about 10 seconds on my Desktop.

Note that we can’t just self.ways.append(way) because way is a temporary/internal Protobuf object and may not be used directly since. Hence, we create our own object that just wraps the extracted contents of way: Its id, its list of nodes (node IDs) and a dictionary containing the tags. Otherwise, you’ll see this error message:

Traceback (most recent call last):
  File "run.py", line 32, in <module>
    trunk_handler.apply_file("kenya-latest.osm.pbf")
RuntimeError: Way callback keeps reference to OSM object. This is not allowed.
Posted by Uli Köhler in Geography, Geoinformatics, OpenStreetMap, Python

How to install cartopy on Ubuntu

First, you need to install libproj (which is a dependency of cartopy) using

sudo apt -y install libproj-dev

After that, install cartopy using

sudo pip3 install cartopy

 

Posted by Uli Köhler in Cartopy, Geoinformatics, Python

How fix Cartopy pip install error ‘Proj 4.9.0 must be installed.’

Problem:

When trying to install cartopy using e.g. pip3 install cartopy you see this error message:

Collecting cartopy
  Downloading Cartopy-0.19.0.post1.tar.gz (12.1 MB)
     |████████████████████████████████| 12.1 MB 13.2 MB/s 
  Installing build dependencies ... done
  Getting requirements to build wheel ... error
  ERROR: Command errored out with exit status 1:
   command: /usr/bin/python3 /tmp/tmp2q7tvpo8 get_requires_for_build_wheel /tmp/tmpfwd5htse
       cwd: /tmp/pip-install-dg2i313t/cartopy
  Complete output (1 lines):
  Proj 4.9.0 must be installed.
  ----------------------------------------
ERROR: Command errored out with exit status 1: /usr/bin/python3 /tmp/tmp2q7tvpo8 get_requires_for_build_wheel /tmp/tmpfwd5htse Check the logs for full command output.

Solution:

Install proj using e.g.

sudo apt -y install libproj-dev

and then install cartopy again using e.g.

sudo pip3 install cartopy

 

Posted by Uli Köhler in Cartopy, Geoinformatics, Python

How to catch ANY exception in gdb

In gdb, run

catch throw

in order to break on any exception that is being thrown.

If you want to only break on a specific exception type instead, use

catch throw [exception type]

 

Posted by Uli Köhler in C/C++

How to compile boost unit test program using CMake

Add this to your CMakeLists.txt:

#
# Compile test suite
#
find_package(Boost COMPONENTS system filesystem unit_test_framework REQUIRED)

add_executable(test_myprogram
    tests/MyTest.cpp
)
target_include_directories(test_myprogram PUBLIC "${CMAKE_CURRENT_LIST_DIR}/include")
target_compile_features(test_myprogram PRIVATE cxx_std_17)

target_link_libraries(test_myprogram
                      ${Boost_FILESYSTEM_LIBRARY}
                      ${Boost_SYSTEM_LIBRARY}
                      ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}
                      )
add_test(test_myprogram test_myprogram)
# make "make test" run the test program
add_custom_target(test
     DEPENDS myprogram
     COMMAND ./test_myprogram
)

In order to build the program and the test, run

make

In order to run the tests, use

make test
Posted by Uli Köhler in Boost, C/C++, CMake

How to fix CMake error Could not find a package configuration file provided by “boost_unit_test_framework”

Problem:

When compiling your CMake-based C++

CMake Error at /usr/lib/x86_64-linux-gnu/cmake/Boost-1.71.0/BoostConfig.cmake:117 (find_package):
  Could not find a package configuration file provided by
  "boost_unit_test_framework" (requested version 1.71.0) with any of the
  following names:

    boost_unit_test_frameworkConfig.cmake
    boost_unit_test_framework-config.cmake

  Add the installation prefix of "boost_unit_test_framework" to
  CMAKE_PREFIX_PATH or set "boost_unit_test_framework_DIR" to a directory
  containing one of the above files.  If "boost_unit_test_framework" provides
  a separate development package or SDK, be sure it has been installed.
Call Stack (most recent call first):
  /usr/lib/x86_64-linux-gnu/cmake/Boost-1.71.0/BoostConfig.cmake:182 (boost_find_component)
  /usr/share/cmake-3.16/Modules/FindBoost.cmake:443 (find_package)
  OCCUtils/CMakeLists.txt:58 (find_package)


-- Configuring incomplete, errors occurred!

Solution:

The error message is trying to tell you that you don’t have the boost unit test library installed (or CMake can’t find it).

On Ubuntu, for example, install it using

sudo apt -y install libboost-test-dev

On Windows you can install it using the prebuilt boost binaries for Windows.

Posted by Uli Köhler in C/C++, CMake

How to fix CMake error Could not find a package configuration file provided by “boost_filesystem”

Problem:

When compiling your CMake-based C++

CMake Error at /usr/lib/x86_64-linux-gnu/cmake/Boost-1.71.0/BoostConfig.cmake:117 (find_package):
  Could not find a package configuration file provided by "boost_filesystem"
  (requested version 1.71.0) with any of the following names:

    boost_filesystemConfig.cmake
    boost_filesystem-config.cmake

  Add the installation prefix of "boost_filesystem" to CMAKE_PREFIX_PATH or
  set "boost_filesystem_DIR" to a directory containing one of the above
  files.  If "boost_filesystem" provides a separate development package or
  SDK, be sure it has been installed.
Call Stack (most recent call first):
  /usr/lib/x86_64-linux-gnu/cmake/Boost-1.71.0/BoostConfig.cmake:182 (boost_find_component)
  /usr/share/cmake-3.16/Modules/FindBoost.cmake:443 (find_package)
  CMakeLists.txt:58 (find_package)

Solution:

The error message is trying to tell you that you don’t have the boost filesystem library installed (or CMake can’t find it).

On Ubuntu, for example, install it using

sudo apt -y install libboost-filesystem-dev

On Windows you can install it using the prebuilt boost binaries for Windows.

Posted by Uli Köhler in C/C++, CMake

Minimal Boost C++ Unit test example

#define BOOST_TEST_MAIN
#include <boost/test/unit_test.hpp>

BOOST_AUTO_TEST_CASE( test1 )
{
    BOOST_CHECK( 2 == 1 );
}

 

Posted by Uli Köhler in C/C++

How to ping gateway in ESP32

You can use the ESP32Ping library in order to easily ping the current gateway IP:

if(Ping.ping(WiFi.gatewayIP(), 1)) { // 1: Just one ping
  // TODO What to do on ping succes
  // Example: Print response time 
  Serial.print(Ping.averageTime()); // Unit: ms
  Serial.println(" ms");
} else {
  // TODO What to do if ping failed?
}

Full example:

#include <Arduino.h>
#include <WiFi.h>

#include <ESP32Ping.h>

void waitForWiFiConnectOrReboot(bool printOnSerial=true) {
  uint32_t notConnectedCounter = 0;
  while (WiFi.status() != WL_CONNECTED) {
      delay(100);
      if(printOnSerial) {
        Serial.println("Wifi connecting...");
      }
      notConnectedCounter++;
      if(notConnectedCounter > 50) { // Reset board if not connected after 5s
          if(printOnSerial) {
            Serial.println("Resetting due to Wifi not connecting...");
          }
          ESP.restart();
      }
  }
  if(printOnSerial) {
    // Print wifi IP addess
    Serial.println("IP address: ");
    Serial.println(WiFi.localIP());
  }
}

#define LED_BUILTIN 2

void setup() {
  Serial.begin(115200);
  WiFi.begin("MyWifiSSID", "MyWifiPassword");
  // Wait for wifi to be connected
  waitForWiFiConnectOrReboot();
  // Initialize LED
  pinMode(LED_BUILTIN,OUTPUT);
}

void loop() {
  if(Ping.ping(WiFi.gatewayIP())) {
    digitalWrite(LED_BUILTIN,HIGH);
    Serial.print(Ping.averageTime());
    Serial.println(" ms");
  } else {
    digitalWrite(LED_BUILTIN, LOW);
    Serial.println("Error :(");
  }

}

Example output

6.12 ms
5.12 ms
5.11 ms
5.16 ms
4.95 ms
4.88 ms
4.84 ms
7.67 ms
5.01 ms
4.87 ms
4.81 ms
4.80 ms
4.85 ms
5.08 ms
5.76 ms
4.54 ms
5.12 ms
2.77 ms
4.88 ms
4.84 ms
6.07 ms
5.08 ms
4.91 ms
6.04 ms
4.88 ms
4.98 ms
6.43 ms
8.18 ms
4.93 ms
5.17 ms
4.97 ms
5.46 ms
5.88 ms
4.78 ms
4.88 ms
6.03 ms
4.84 ms
5.70 ms
5.94 ms
7.25 ms
5.07 ms
4.78 ms
5.51 ms
4.99 ms
5.04 ms
4.79 ms
4.94 ms
4.81 ms
5.97 ms
5.85 ms
4.83 ms
4.80 ms
4.80 ms
6.29 ms
4.99 ms
5.04 ms
9.21 ms
5.20 ms
6.05 ms
6.14 ms
5.03 ms
4.90 ms
7.22 ms
5.06 ms
4.94 ms
9.03 ms
5.13 ms
11.97 ms
6.32 ms
6.12 ms
4.92 ms
4.92 ms
6.01 ms
4.96 ms
4.98 ms
4.94 ms
6.08 ms
6.11 ms
4.93 ms
5.05 ms
5.78 ms
4.47 ms
6.28 ms
5.02 ms
5.13 ms
5.11 ms
5.19 ms
8.89 ms
5.76 ms
5.18 ms
8.08 ms
4.97 ms
4.89 ms
4.70 ms
5.40 ms
7.46 ms
5.09 ms
4.95 ms
4.96 ms
5.01 ms
5.01 ms
4.89 ms
6.22 ms
6.76 ms
6.92 ms
6.10 ms
9.61 ms
5.29 ms
6.13 ms
5.15 ms
5.02 ms
5.03 ms
5.01 ms
6.13 ms
4.78 ms
3.90 ms
6.27 ms
8.07 ms
5.94 ms
4.50 ms
6.13 ms
4.99 ms
6.07 ms
4.80 ms
4.84 ms
4.95 ms
4.95 ms
6.78 ms
4.88 ms

 

Posted by Uli Köhler in C/C++, ESP8266/ESP32, Networking

How to fix Jupyter Hub Exception: Jupyter command `jupyter-hub` not found.

Problem:

When trying to start jupyter hub, you see this error message:

[email protected]:~$ jupyter hub
Traceback (most recent call last):
  File "/usr/local/bin/jupyter", line 8, in <module>
    sys.exit(main())
  File "/usr/local/lib/python3.8/dist-packages/jupyter_core/command.py", line 285, in main
    command = _jupyter_abspath(subcommand)
  File "/usr/local/lib/python3.8/dist-packages/jupyter_core/command.py", line 124, in _jupyter_abspath
    raise Exception(
Exception: Jupyter command `jupyter-hub` not found.

Solution:

Remove the space between jupyter and hub. Instead of

jupyter hub

run

jupyterhub

Example:

[email protected] $ jupyterhub                           
[I 2021-06-19 02:37:12.766 JupyterHub app:2463] Running JupyterHub version 1.4.1
[I 2021-06-19 02:37:12.766 JupyterHub app:2493] Using Authenticator: jupyterhub.auth.PAMAuthenticator-1.4.1
[I 2021-06-19 02:37:12.766 JupyterHub app:2493] Using Spawner: jupyterhub.spawner.LocalProcessSpawner-1.4.1
[I 2021-06-19 02:37:12.766 JupyterHub app:2493] Using Proxy: jupyterhub.proxy.ConfigurableHTTPProxy-1.4.1
[I 2021-06-19 02:37:12.773 JupyterHub app:1534] Loading cookie_secret from /home/uli/dev/jupyterhub_cookie_secret
[I 2021-06-19 02:37:12.788 JupyterHub proxy:497] Generating new CONFIGPROXY_AUTH_TOKEN
[W 2021-06-19 02:37:12.789 JupyterHub app:1808] No admin users, admin interface will be unavailable.
[W 2021-06-19 02:37:12.789 JupyterHub app:1809] Add any administrative users to `c.Authenticator.admin_users` in config.
[I 2021-06-19 02:37:12.789 JupyterHub app:1838] Not using allowed_users. Any authenticated user will be allowed.
[I 2021-06-19 02:37:12.823 JupyterHub app:2530] Initialized 0 spawners in 0.001 seconds
[W 2021-06-19 02:37:12.825 JupyterHub proxy:699] Running JupyterHub without SSL.  I hope there is SSL termination happening somewhere else...
[I 2021-06-19 02:37:12.825 JupyterHub proxy:703] Starting proxy @ http://:8000
02:37:12.946 [ConfigProxy] info: Proxying http://*:8000 to (no default)
02:37:12.948 [ConfigProxy] info: Proxy API at http://127.0.0.1:8001/api/routes
02:37:12.981 [ConfigProxy] info: 200 GET /api/routes 
[I 2021-06-19 02:37:12.981 JupyterHub app:2778] Hub API listening on http://127.0.0.1:8081/hub/
02:37:12.982 [ConfigProxy] info: 200 GET /api/routes 
[I 2021-06-19 02:37:12.983 JupyterHub proxy:347] Checking routes
[I 2021-06-19 02:37:12.983 JupyterHub proxy:432] Adding route for Hub: / => http://127.0.0.1:8081
02:37:12.984 [ConfigProxy] info: Adding route / -> http://127.0.0.1:8081
02:37:12.985 [ConfigProxy] info: Route added / -> http://127.0.0.1:8081
02:37:12.986 [ConfigProxy] info: 201 POST /api/routes/ 
[I 2021-06-19 02:37:12.986 JupyterHub app:2853] JupyterHub is now running at http://:8000

 

Posted by Uli Köhler in Python

How to run Jupyter Hub (multi-user mode) using systemd

The following script will install Jupyter Hub in single user mode (i.e. only a single Linux user can login to Jupyter Hub using the web interface).

Prerequisites

First install Python & PIP, then NodeJS, then Jupyter Hub, then configurable-http-proxy :

curl -sL https://deb.nodesource.com/setup_14.x | sudo -E bash -
sudo apt-get install -y nodejs python3-pip
sudo pip3 install jupyterhub
sudo npm i -g configurable-http-proxy

Installing the Jupyter Hub systemd service

Run the following script using sudo!

#!/bin/bash
# This script installs and enables/starts the JupyterHub systemd service
export NAME=JupyterHub

# Create service file
cat >/etc/systemd/system/${NAME}.service <<EOF
[Unit]
Description=${NAME}

[Service]
Type=simple
ExecStart=/usr/bin/env jupyterhub --port=11569
User=root
Group=root

Restart=on-failure
RestartSec=5s

[Install]
WantedBy=multi-user.target
EOF

# Enable and start service
systemctl enable --now ${NAME}

This script will install a systemd service named JupyterHub autostart it on boot. It is running on port 11569 by default.

Posted by Uli Köhler in Python, systemd

How to run Jupyter Hub (single user mode) using systemd for autostart

Note: This will only allow a single (preconfigured) user to login to Jupyter lab! See How to run Jupyter Hub (multi-user mode) using systemd on how to deploy Jupyter Hub in multi-user mode using systemd, which allows any Unix user to login!

The following script will install Jupyter Hub in single user mode (i.e. only a single Linux user can login to Jupyter Hub using the web interface).

Prerequisites

First install Python & PIP, then NodeJS, then Jupyter Hub, then configurable-http-proxy :

curl -sL https://deb.nodesource.com/setup_14.x | sudo -E bash -
sudo apt-get install -y nodejs python3-pip
sudo pip3 install jupyterhub
sudo npm i -g configurable-http-proxy

Installing the Jupyter Hub systemd service

Run the following script as the user you want to be able to login! Do not run the script using sudo !

#!/bin/bash
# This script installs and enables/starts a systemd service
export NAME=JupyterHub-$USER
export GROUP=$(id -gn $USER)
# Create service file
sudo tee /etc/systemd/system/${NAME}.service <<EOF
[Unit]
Description=${NAME}

[Service]
Type=simple
ExecStart=/usr/bin/env jupyterhub

WorkingDirectory=$HOME
User=$USER
Group=$GROUP

Restart=on-failure
RestartSec=5s

[Install]
WantedBy=multi-user.target
EOF

# Enable and start service
systemctl enable --now ${NAME}

This script will install a systemd service named JupyterHub-$USER (where $USER is the current user, e.g. uli) and autostart it on boot.

Running multiple services

If you run multiple services, you can run the script for each user and choose a unique port for each service by adding --port=7219 to the /usr/bin/env jupyter hub command, e.g.

ExecStart=/usr/bin/env jupyterhub --port=7192

Alternatively, you can run a single systemwide Jupyter Hub in multi-user mode where multiple users can log in.

Posted by Uli Köhler in Python, systemd

How to fix Jupyter Hub No such file or directory: ‘configurable-http-proxy’

Problem:

When trying to start Jupyter Hub, you see an error message like

[E 2021-06-18 13:07:48.462 JupyterHub proxy:711] Failed to find proxy ['configurable-http-proxy']
    The proxy can be installed with `npm install -g configurable-http-proxy`.To install `npm`, install nodejs which includes `npm`.If you see an `EACCES` error or permissions error, refer to the `npm` documentation on How To Prevent Permissions Errors.
[C 2021-06-18 13:07:48.462 JupyterHub app:2739] Failed to start proxy
    Traceback (most recent call last):
      File "/usr/local/lib/python3.8/dist-packages/jupyterhub/app.py", line 2737, in start
        await self.proxy.start()
      File "/usr/local/lib/python3.8/dist-packages/jupyterhub/proxy.py", line 707, in start
        self.proxy_process = Popen(
      File "/usr/lib/python3.8/subprocess.py", line 854, in __init__
        self._execute_child(args, executable, preexec_fn, close_fds,
      File "/usr/lib/python3.8/subprocess.py", line 1702, in _execute_child
        raise child_exception_type(errno_num, err_msg, err_filename)
    FileNotFoundError: [Errno 2] No such file or directory: 'configurable-http-proxy'

Solution:

As outlined in the Jupyter Hub installation docs, you need to install the configurable-http-proxy npm package in order for Jupyter to work:

sudo npm install -g configurable-http-proxy

In case you haven’t installed npm, see our article on How to install NodeJS 14.x LTS on Ubuntu in 1 minute or find the appropriate distribution for your OS on the NodeJS download page.

Posted by Uli Köhler in Python