Nono.MA

JULY 23, 2021

This page is incomplete.

I'd love to be able to export asciinema recordings as gif animations and mp4 videos. The creators don't see a point in doing this, as converting text-based recordings into image-based animations goes against asciinema's raison d'etre, but I would find it super useful to be able to include small snippets of recordings on Keynote or PowerPoint presentation slides.

JULY 15, 2021

Here are a few helper functions to list Lambda functions and layers (and to count them) using the AWS Command Line Interface (AWS CLI) to inspect the serverless resources of your Amazon Web Services (AWS) account.

Listing Lambda Layers of a Function

aws lambda get-function --function-name {name|arn} | \
jq .Configuration.Layers
[
  {
    "Arn": "arn:aws:lambda:us-west-2:00000000:layer:layer-name:1",
    "CodeSize": 1231231
  }
]

Counting Lambda Layers of a Function

aws lambda get-function --function-name {name|arn} | \
jq '.Configuration.Layers | length'
# Returns 1 (or number of layers attached to function)

Counting Lambda Layers in an AWS account

aws lambda list-layers | \
jq '.Layers | length'
# Returns 4 (or number of layers in your account)

Listing All Layers in an AWS account

aws lambda list-layers
{
    "Layers": [
        {
            "LayerName": "layer-name",
            "LayerArn": "arn:aws:lambda:us-west-2:0123456789:layer:layer-name",
            "LatestMatchingVersion": {
                "LayerVersionArn": "arn:aws:lambda:us-west-2:0123456789:layer:layer-name:1",
                "Version": 1,
                "Description": "Layer Description",
                "CreatedDate": "2021-07-14T14:00:27.370+0000",
                "CompatibleRuntimes": [
                    "python3.7"
                ],
                "LicenseInfo": "MIT"
            }
        },
        {
            "LayerName": "another-layer-name",
            "LayerArn": "arn:aws:lambda:us-west-2:0123456789:layer:another-layer-name",
            "LatestMatchingVersion": {
                "LayerVersionArn": "arn:aws:lambda:us-west-2:0123456789:layer:another-layer-name:4",
                "Version": 4,
                "Description": "Layer Description",
                "CreatedDate": "2021-07-14T11:41:45.520+0000",
                "CompatibleRuntimes": [
                    "python3.6"
                ],
                "LicenseInfo": "MIT"
            }
        }
    ]
}

Listing Lambda Functions in an AWS account

aws lambda list-functions
{
    "Functions": [
        {
            "FunctionName": "function-name",
            "FunctionArn": "arn:aws:lambda:us-west-2:0123456789:function:function-name",
            "Runtime": "python3.7",
            "Role": "arn:aws:iam::0123456789:role/role-name",
            "Handler": "lambda_function.lambda_handler",
            "CodeSize": 1234,
            "Description": "Function description.",
            "Timeout": 30,
            "MemorySize": 128,
            "LastModified": "2021-07-14T16:48:19.052+0000",
            "CodeSha256": "28ua8s0aw0820492r=",
            "Version": "$LATEST",
            "Environment": {
                "Variables": {
                }
            },
            "TracingConfig": {
                "Mode": "PassThrough"
            },
            "RevisionId": "1b0be4c3-4eb6-4254-9061-050702646940",
            "Layers": [
                {
                    "Arn": "arn:aws:lambda:us-west-2:0123456789:layer:layer-name:1",
                    "CodeSize": 1563937
                }
            ],
            "PackageType": "Zip"
        }
    ]
}

JULY 8, 2021

This page is incomplete

An option is to set CACHE=array on your .env file.

I need to learn why this happens and what other alternatives there are to avoid this error.

This happened to me when installing laravel-geoip https://github.com/Torann/laravel-geoip/issues/123

It makes sense that you don't want to change your cache type if you're using file or database to array simply to use a function in this package.

Other users recommended to publish tarenn/geoip's config file and disable caching and tagging.

# Publish the configuration file
php artisan vendor:publish --provider="Torann\GeoIP\GeoIPServiceProvider" --tag=config
# Copied File [/vendor/torann/geoip/config/geoip.php] To [/config/geoip.php]
# Publishing complete.
    // ...
    'cache' => 'none', // defaults to 'all'
    // ...
    'cache_tags' => [], // defaults to // ['torann-geoip-location']
    // ...

Another option is to conditionally set the tag.

    // ...
    'cache_tags' => env('CACHE_DRIVER') == "array" ? ['torann-geoip-location'] : null,
    // ...

JUNE 30, 2021

I get this error after running docker run --rm -it IMAGE_TAG.

The problem was that the image I was using wasn't really an image to execute in Docker but a set of steps to build a Python wheel package (.whl).

The solution was to only build the image specifying an --output directory to which the resulting wheel file could be copied.

DOCKER_BUILDKIT=1 docker build --output folder_to_save_wheel .

JUNE 19, 2021

To avoid ImageMagick from interpolating pixels when you want a sharp resize method (equivalent to PIL's Image.NEAREST_NEIGHBOR) you can use mogrify and set the -filter to point.

# Assuming we're upscaling an image smaller than 2000x2000 pixels
mogrify -resize 2000x2000 -filter point image.png

MAY 17, 2021

dyld: Library not loaded: /usr/local/opt/openldap/lib/libldap-2.4.2.dylib
dyld: Library not loaded: /opt/homebrew/opt/icu4c/lib/libicuio.68.dylib
  Referenced from: /opt/homebrew/bin/php
  Reason: image not found
zsh: abort      composer

Install (or update) the Xcode developer tools.

xcode-select --install

Reinstall icu4c.

brew reinstall icu4c

Make sure no errors prevent Homebrew from installing icu4c properly. For instance, I had to remove a few php folders and re-run the brew reinstall icu4c command.

sudo rm -rf /opt/homebrew/Cellar/php@7.4/7.4.15
sudo rm -rf /opt/homebrew/Cellar/php/8.0.2

MAY 13, 2021

Here's a way to map a given color in a Pillow image (PIL.Image) to another color. This is not the fastest method and it will only replace exact matches.

In this example, we're turning all black pixels in the input image (0,0,0) with blue (0,0,255).

from PIL import Image
import numpy as np

img = Image.open('/path/to/image.png')
img[np.where((img==[0,0,0]).all(axis=2))] = [0,0,255]
img.show()

MAY 13, 2021

from PIL import Image

img = Image.open('/path/to/image')

left = 10
top = 20
right = 10
bottom = 20

img = img.crop((left, top, right, bottom))

MAY 13, 2021

import json

file = open('my-file.json')
obj = json.load(file)

MAY 8, 2021

After CVAT is running and you have access to its login screen, you need to have an admin account to log in and access the admin panel.

You create an admin user from the command-line interface.

docker exec -it cvat bash -ic 'python3 ~/manage.py createsuperuser'

This command will as you for a username, email, and password.

After you log in, you can hover your username (in the top-right corner of the screen) and select "Admin page" to access the Django admin panel, where you can manage your CVAT site, manage users, groups, and more.

MAY 1, 2021

When you use two-factor authentication to sign in to your Gmail account (or to "Sign in with Google") you access your account with your email, password, and a verification code generated by Google Authenticator or other authenticator apps (such as Duo).

You might get an error like the one that follows when trying to sign in to Gmail with your Google password.

Authentication failed. Please check your username/password and Less Secure Apps access for mail@example.com.
Server returned error: "534-5.7.9 Application-specific password required. Learn more at 534 5.7.9 https://support.google.com/mail/?p=InvalidSecondFactor l25sm248619lfe.188 - gsmtp , code: 534"

When the service you're trying to use your Gmail account with doesn't allow you to "Sign in with Google," you need to create an app-specific password as detailed in the support Url provided by the error message.

Create a Google App Password

This app password

  • Go to your Google account
  • Security
  • Sign in to Google
  • App passwords
  • Choose the service type — e.g., Mail, Calendar, Contacts, YouTube, or Other (custom)
  • Choose the device type — e.g., iPhone, iPad, Mac, Windows, etc.
  • Generate

You'll get an app-specific password like this one — dbkdwckcplvgaktc — that will let you log in to the authorized service with your email and this password.

In my case, I use this password to be able to "Send as" from Gmail from an email address that has two-factor authentication turned on.

APRIL 28, 2021

import torch
print(torch.__version__)

APRIL 20, 2021

cd /path/to/repo.git
sudo chgrp -R {groupname} .
sudo chmod -R g+rwX .
find . -type d -exec chmod g+s '{}' +

Source

LAST UPDATED MAY 20, 2021

If you're receiving this error when trying to composer install.

Your GitHub OAuth token for github.com contains invalid characters

Updating Composer

2021.05.20 · Update

The solution is to update Composer to the latest version, which supports the new token format, as suggested by Jordi Boggiano on this tweet. "Composer 1.10.21 and 2.0.12 (both released April 1st) added support for the new GitHub token format."

As of this writing, the following command will install the latest version of Composer on your machine (i.e., 2.0.13). Note that future Composer updates will break the script as shown here, as the hash check won't pass.

php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
php -r "if (hash_file('sha384', 'composer-setup.php') === '756890a4488ce9024fc62c56153228907f1545c228516cbf63f885e036d37e9a59d27d63f46af1d4d07ee0f76181c7d3') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
php composer-setup.php
php -r "unlink('composer-setup.php');"

On macOS, you can use Homebrew to install (or reinstall) composer.

brew install composer
brew reinstall composer

The brute-force fix

2021.04.20

As I mentioned above, both Lukas Kahwe Smith and Jordi Boggiano discouraged tinkering with Composer's auth.json file manually and recommended upgrading Composer to its latest version instead.

Still, here's the brute-fox fix that worked for me. Apparently, editing the auth.json is the only way to update to the latest Composer programmatically, and you can revert it to its original state if you opt for this option. The alternative, of course, is to upgrade as shown above.

Edit the composer authentication configuration file ~/.composer/auth.json.

nano ~/.composer/auth.json

Then replace the following.

  "github-oauth": {
    "github.com": "ghp_[YOUR-PERSONAL-TOKEN]"
  }

With this (basic auth):

  "http-basic": {
    "github.com": {
      "username": "[YOUR-GITHUB-USERNAME]",
      "password": "ghp_[YOUR-PERSONAL-TOKEN]"
    }
  }

Source

Thanks

To Lukas Kahwe Smith and Jordi Boggiano for pointing this out on Twitter.

APRIL 20, 2021

I found this error while trying to update and install composer packages with composer install.

could not find driver (SQL: select * from information_schema.tables where table_schema = folio_burns and table_name = folio_items and table_type = 'BASE TABLE')

At first, I thought the solution was to edit /etc/php/7.4/cli/php.ini (for PHP-FPM 7.4 in my case) and uncomment the line ;extension=pdo_mysql to be like extension=pdo_mysql . But I was still getting this error as the mysql extension was missing.

PHP Warning:  PHP Startup: Unable to load dynamic library 'pdo_mysql' (tried: /usr/lib/php/20190902/pdo_mysql (/usr/lib/php/20190902/pdo_mysql: cannot open shared object file: No such file or directory), /usr/lib/php/20190902/pdo_mysql.so (/usr/lib/php/20190902/pdo_mysql.so: cannot open shared object file: No such file or directory)) in Unknown on line 0

The solution ended up being to install the extension, which would also add its own .ini file and activate itself on installation.

sudo apt-get install -y php7.4-mysql

Note that you can run this command with multiple extensions to be installed at once.

sudo apt-get install -y php7.4-{xml,bcmath,gd,mbstring,xsl,zip,curl,mysql}

APRIL 15, 2021

After downloading a website as HTML with cURL or any other workflow, you can convert the HTML code to the Markdown syntax with pandoc.

pandoc -o output.md input.html

APRIL 15, 2021

You can download any website as an HTML file (without the site's assets) using cURL in the command line, using the -L flag to follow any existing redirects.

curl -L https://nono.ma --output nono-ma.html

The manual alternative is to right-click on a website on your browser of choice (say, Google Chrome or Firefox), select Save As.., and save the site as HTML with some of its assets in a subfolder.

Afterwards, you can convert the downloaded HTML page into a Markdown document with pandoc.

APRIL 14, 2021

Here's how to get the raw String value of a Stringable object. Laravel's Illuminate\Support\Stringable has lots of helper functions, but sometimes you want to get the raw string value. For that, you just need to use the strval PHP built-in function on an object of the Stringable class.

// Define Strintable object
$stringable = Str::of('laravel-stringable-to-string');
get_class($stringable); // returns Illuminate\Support\Stringable
gettype($stringable); // returns object

// Get its raw String value
$string = strval($stringable);
get_class($string); // returns PHP Warning:  get_class() expects parameter 1 to be object, string given in […]
gettype($string); // returns string

MARCH 28, 2021

I got a sudden fan shut down every time I would switch on my MacBook Pro (16-inch, 2019) after I upgraded macOS from Catalina to Big Sur. Here's how I fixed it.

  • CMD + R - Press on reboot to enter Rescue Mode
  • Login as a user you know the password for.
  • Utilities › Terminal
  • Run kmutil trigger-panic-medic --volume-root /Volumes/Macintosh\ HD
  • Start the computer normally

This is the Kext extension that, apparently, was causing the issue.

cd /System/Library/Extensions/AppleThunderboltNHI.kext

MARCH 19, 2021

If you're getting this message when encrypting files with a GnuPG—the GNU Pretty Good Privacy (PGP) package—you can mark your key as trusted (if that's the case). This often happens when you copy a trusted key from one machine to another.

Here's what I was getting before 'trusting' my own key.

gpg: <KEY_ID>: There is no assurance this key belongs to the named user
It is NOT certain that the key belongs to the person named
in the user ID.  If you *really* know what you are doing,
you may answer the next question with yes.

Use this key anyway? (y/N) y

Every time, I'd have to answer y to complete the encryption process.

I learned how to skip this step from this StackOverflow post.

gpg --edit-key <KEY_ID>
gpg › trust

Then you set the degree to which you trust the specified key.

1 = I don't know or won't say
2 = I do NOT trust
3 = I trust marginally
4 = I trust fully
5 = I trust ultimately
m = back to the main menu

I selected 5 as this is a key I created for myself.

Your decision? 5
Do you really want to set this key to ultimate trust? (y/N) y

Confirm, then quit.

gpg › quit

Now you can use your GPG key without confirm every operation.

MARCH 8, 2021

If you multiple Vue components of the same type throughout your application but they're not in the same parent DOM element, mounting them may not be as easy as calling their parent.

Mounting multiple Vue components with a shared parent

In this first example, we can mount all of our button-counter components by mounting their parent, #button-container.

<div id="button-container">
  <button-counter></button-counter>
  <button-counter></button-counter>
  <button-counter></button-counter>
</div>

We just need to make one call.

new Vue({ el: '#button-container' });

But what if our components are spread throughout different HTML elements?

Mount multiple Vue components without a shared parent

Imagine you have components of the same (or different) types spread throughout your website's DOM.

<div>
  <button-counter></button-counter>
  <button-counter></button-counter>
</div>

<div>
  <button-counter></button-counter>
</div>

<button-counter></button-counter>

We can iterate through the DOM elements and mount them one by one.

// Obtain all button-counter elements
let elements = document.getElementsByTagName('button-counter');

// Convert HTMLCollection to array
let arr = Array.prototype.slice.call( elements )

// Loop through array and mount all elements
arr.forEach((el) => {
    new Vue( { el });
});

Note that we had to convert the HTMLCollection to a JavaScript array before we could call the .forEach() function to iterate over the button-counter host DOM nodes.

This works with Vue 2, and Vue 3 recently released new features for multi-root components and optional TypeScript support.

MARCH 8, 2021

Change this.

window.Vue = require('vue');

For this.

window.Vue = require('vue').default;

Then this should work.

window.Vue = require('vue').default;
window.VueResource = require('vue-resource');
window.Vue.use(window.VueResource);

MARCH 8, 2021

When trying to use the newest features of Laravel 8.x to automatically install Babel plugins with the .vue() call after using mix.js(), I kept getting the following error.

AssertionError [ERR_ASSERTION]: mix.js() is missing required parameter 1: entry

Even though I was using Laravel 8.x, my application had been migrated from older versions and my JavaScript dependencies in package.json were old. Specifically, I was still using Laravel Mix 5 while the newest version 6 had been released.

The solution was to change to version ^6.0.6 in package.json and run npm install, as the .vue() call is a new feature in Laravel Mix 6.

    "devDependencies": {
        // ...
        "laravel-mix": "^6.0.6",
        // ...
    },

Then on webpack.mix.js.

mix.js('resources/js/app.js', 'public/js')
   .vue();

I could then run the following commands successfully.

npm run dev
npm run watch
npm run hot
npm run prod

I believe this works with Vue 2 and Vue 3, and that the Vue version wasn't the problem but the version of Mix. (Note that I had also run npm update previously to update other dependencies and had to remove the --inline, --hide-modules and --no-progress options from my development and production commands in the scripts property of package.json.)

MARCH 5, 2021

This script re-encodes the seconds between second 25 and 45 of an input.mov video from mov to mp4; rotates the frames 180 (the hflip,vflip flags flip all pixels vertically and horizontally); removes the audio (with the -an flag); and speeds up the video (i.e., skips frames, with the flag setpts=0.05*PTS, which you could adjust to have more frames, for instance, as setpts=0.1*PTS).

ffmpeg -ss 00:00:25 -to 00:00:45 \
       -i "input.mov" \
       -an -filter:v "hflip,vflip,setpts=0.05*PTS" \
       "output.mp4"

FEBRUARY 25, 2021

Batch-Export PowerPoint Slides to Images Programmatically

unoconv is a tool to "convert between any document format supported by OpenOffice," available to install via Homebrew on macOS. You can convert, for instance, ppt files to png images (or to a multi-page PDF files) by running a command with this command-line interface program. The project is open source and you can browse its code on GitHub.

Install unoconv with Homebrew

brew install unoconv

Common issues: LibreOffice not found on your system

I ran into this issue when I first ran the unoconv command.

unoconv
# unoconv: Cannot find a suitable office installation on your system.
# ERROR: Please locate your office installation and send your feedback to:
#        http://github.com/dagwieers/unoconv/issues

That's because unoconv can't find libreoffice. You can install its Homebrew Cask.

brew install --cask libreoffice

After doing that, unoconv can find the libreoffice installation.

unoconv
# unoconv: you have to provide a filename or url as argument
# Try `unoconv -h' for more information.

Export PowerPoint Slides to PDF

unoconv slides.pptx -f pdf

Convert PDF to PNG or JPG Images

Even though you can directly export a PowerPoint presentation to JPEG or PNG format, unoconv exports only the first page by default.

You can use ImageMagick's convert tool to rasterize the PDF pages as images.

convert -density 300 slides.pdf image%d.jpg

Batch-convert Presentations to Images

Here's a bash script that will convert all ppt presentations in a folder to jpg images by folders.

# Convert all pptx files to multi-page pdf files
unoconv -f pdf *.pptx

# Loop through pptx files
for f in *.pptx
do
    echo "${f}.."
    mkdir -p ${f}-jpg
    convert -density 20 ${f%.*}.pdf "./${f}-jpg/image%d.jpg"
done

Available formats

You can see the extensive list of supported input and output formats on unoconv's documentation and read more about how to use unoconv in its help manual page or by running unoconv -h.

FEBRUARY 24, 2021

To read environment variables from a Python script or a Jupyter notebook, you would use this code—assuming you have a .env file in the directory where your script or notebook lives.

# .env
FOO=BAR
S3_BUCKET=YOURS3BUCKET
S3_SECRET_KEY=YOURSECRETKEYGOESHERE
# script.py
import os
print(os.environ.get('FOO')) # Empty

But this won't return the value of the environment variables, though, as you need to parse the contents of your .env file first.

For that, you can either use python-dotenv.

pip install python-dotenv

Then use this Python library to load your variables.

# Example from https://pypi.org/project/python-dotenv/
from dotenv import load_dotenv
load_dotenv()

# OR, the same with increased verbosity
load_dotenv(verbose=True)

# OR, explicitly providing path to '.env'
from pathlib import Path  # Python 3.6+ only
env_path = Path('.') / '.env'
load_dotenv(dotenv_path=env_path)

# Print variable FOO
print(os.environ.get('FOO')) # Returns 'BAR'

Or load the variables manually with this script.

import os
env_vars = !cat ../script/.env
for var in env_vars:
    key, value = var.split('=')
    os.environ[key] = value

# Print variable FOO
print(os.environ.get('FOO')) # Returns 'BAR'

FEBRUARY 15, 2021

Here's how I installed pandoc on my MacBook Pro (13–inch, M1, 2020) to run with Rosetta 2 — not natively, but on the x86_64 architecture — until a universal binary for macOS is built that supports the arm64 architecture in new Appple Silicon Macs.

This guide may be used to install other non-universal brew packages.

# Install Homebrew for x86_64 architecture
# https://soffes.blog/homebrew-on-apple-silicon
arch -x86_64 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
# Install pandoc using that version of Homebrew
arch -x86_64 /usr/local/bin/brew install pandoc

Outputs

==> Downloading https://homebrew.bintray.com/bottles/pandoc-2.11.4.big_sur.bottle.tar.gz
Already downloaded: /Users/nono/Library/Caches/Homebrew/downloads/34e1528919e624583d70b1ef24381db17f730fc69e59144bf48abedc63656678--pandoc-2.11.4.big_sur.bottle.tar.gz
==> Pouring pandoc-2.11.4.big_sur.bottle.tar.gz
🍺  /usr/local/Cellar/pandoc/2.11.4: 10 files, 146.0MB
# Check pandoc's version
arch -x86_64 pandoc --version

Outputs

pandoc 2.11.4
Compiled with pandoc-types 1.22, texmath 0.12.1, skylighting 0.10.2,
citeproc 0.3.0.5, ipynb 0.1.0.1
User data directory: /Users/nono/.local/share/pandoc or /Users/nono/.pandoc
Copyright (C) 2006-2021 John MacFarlane. Web:  https://pandoc.org
This is free software; see the source for copying conditions. There is no
warranty, not even for merchantability or fitness for a particular purpose.

Converting Markdown to Html

arch -x86_64 pandoc sample.md -o sample.html

Contents of sample.md:

# Hello, Apple Silicon!

- Pandoc
- seems
- to
- work.

Contents of sample.html:

<h1 id="hello-apple-silicon">Hello, Apple Silicon!</h1>
<ul>
<li>Pandoc</li>
<li>seems</li>
<li>to</li>
<li>work.</li>
</ul>

FEBRUARY 9, 2021

When running any git command — including git pull, git push, git status, etc. — I was getting this error on macOS Big Sur.

xcrun: error: invalid active developer path (/Library/Developer/CommandLineTools), missing xcrun at: /Library/Developer/CommandLineTools/usr/bin/xcrun

The message means the Xcode Developer Tools are not properly installed, and you need to run the following command to fix this.

xcode-select --install

A window will prompt you to Install the Developer Tools. After two minutes, a message saying "The software was installed" showed up on my machine, a MacBook Pro (13-inch, M1, 2020). I was good to go.

JANUARY 20, 2021

Supposing you've started your container with ./docker-wine wine notepad and saved your files to your volume, for instance, at My Music folder with the new.txt name.

docker cp wine:/home/wineuser/new.txt ~/Desktop/

Want to see older publications? Visit the archive.

Listen to Getting Simple .