Welcome to MoonSet One Docs

Objectives: Docs with tips or tricks within Python, Ruby, Linux, Data Ops, Data Engineering, some Javascript and Frontends.

This is only scripts with minimal explanation. The full explanation is going on my blog.

TILs: "Today I learned" are simple tricks that you annotated to remind yourself on a later date, or share with fellow devs.

ArchLinux | Manjaro Based Setup

Tips and tricks for a ideal initial setup

Ranking mirrors for faster downloads

Manjaro

 sudo pacman-mirrors -c Brazil,United_States

ArchLinux

curl -s "https://www.archlinux.org/mirrorlist/?country=BR&use_mirror_status=on" | sed -e 's/^#Server/Server/' -e '/^#/d' | rankmirrors -n 5 -

Update system

sudo pacman -Suy sudo pacman -S yay # only works on Manjaro

Interesting terminal companions and programming languages

yay -S micro-manjaro
yay -S --needed thefuck nodenv nodenv-node-build-git rbenv ruby-build elixir fish tmux autoconf automake bison bind-tools fasd htop make patch ed fzf gcc mosh ruby tk yarn php php-fpm python-pip python-pillow python-numpy nfs-utils lsof strace tldr mosh
tldr --update

Interesting graphical interface apps and tools

yay -S --needed adobe-source-sans-pro-fonts chromium deluge firefox-developer-edition otf-fira-code otf-fira-sans p7zip ttf-roboto ttf-ubuntu-font-family
yay -S --needed albert-lite atom-editor-bin visual-studio-code-bin ttf-iosevka pinta simple-scan xsel python-pyusb

Interesting python packages

Interesting utilities

yay -S --needed telegram-desktop foliate flameshot neofetch

No password for sudo

sudo visudo -f /etc/sudoers.d/99-mine

Be careful with %wheel, I prefer to put just myself :D

%wheel ALL=(ALL) NOPASSWD: ALL
rodrigo ALL=(ALL) NOPASSWD: ALL

Using my .dotfiles with TMUX and Fish Shell

```sh
cd
git clone https://github.com/tmux-plugins/tpm ~/.tmux/plugins/tpm
git clone https://gitlab.com/rdlu/dotfiles.git .dotfiles
ln -s ~/.dotfiles/tmux.conf .tmux.conf
tmux source ~/.tmux.conf
ln -s ~/.dotfiles/fish/config.fish ~/.config/fish/config.fish
ln -s ~/.dotfiles/fish/omf ~/.config/omf
curl -L https://get.oh-my.fish | fish
sudo chsh (whoami) -s /usr/bin/fish
```

After that restart your fish prompt and:

omf update

CTRL+A; SHIFT+U for tmux plugins

Fixes and Workarounds for ArchLinux

I can use in emergencies or having bugs.

Using dnsmasq like in Ubuntu for DNS resolutions

It's useful when using Docker containers + Reverse Proxies. You can just add your local address for wildcard resolution later.

sudo pacman -S dhclient dnsmasq

Then configure NetworkManager:

micro /etc/NetworkManager/NetworkManager.conf

To this:

[main]
plugins=keyfile
dhcp=dhclient
dns=dnsmasq

Rereading /etc/hosts and restarting:

echo 'addn-hosts=/etc/hosts' | sudo tee /etc/NetworkManager/dnsmasq.d/hosts.conf > /dev/null
sudo systemctl restart NetworkManager

Adding wildcard subdomains to dnsmasq

Create/edit a NetworkManager configuration for dnsmasq hosts:

sudo micro /etc/NetworkManager/dnsmasq.d/hosts.conf

Add, changing the domain to your preference:

address=/domain.tld/127.0.0.3
address=/sub.domain.tld/127.0.0.4

Restart NetworkManager

sudo systemctl restart NetworkManager

Comparing new configuration files after updates

SUDO_EDITOR=meld sudo -e /etc/file{,.pacnew}

Bluetooth AD2P not working with Gnome

yay -S pulseaudio-bluetooth-a2dp-gdm-fix

Firefox Text fields: Using dark themes with Gnome

Create a new property using about:config

Name: widget.content.gtk-theme-override Value: Adwaita:light

Adwaita must be present, KDE users can rely on breeze.

Firefox versus HiDPI and Wayland

Firefox Developer Edition

cp /usr/share/applications/firefox-developer-edition.desktop ~/.local/share/applications/
sed -i 's/Exec=/Exec=env MOZ_ENABLE_WAYLAND=1 /g' ~/.local/share/applications/firefox-developer-edition.desktop

Regular Firefox

cp /usr/share/applications/firefox.desktop ~/.local/share/applications/
sed -i 's/Exec=/Exec=env MOZ_ENABLE_WAYLAND=1 /g' ~/.local/share/applications/firefox.desktop

Undoing

rm ~/.local/share/applications/firefox*

Slow Playback / Hardware Acceleration

If your video playback is slow, change in about:config this setting: layers.acceleration.force-enabled to true

It works just fine on my three main computers (AMD A8 with open source radeon/mesa; Intel i5 with iGPU and mesa; Intel i7 with nVidia GTX 1050Ti and proprietary nvidia)

GPG, SSH Auth with Keybase

Installing requirements:

sudo pacman -S kbfs keybase keybase-gui micro

If you are having problems with gpg-agent as ssh agent mode, restart gnupg from scratch (export your important keys first)

rm -rf ~/.gnupg

Login to Keybase using the GUI or keybase login. Now import private key and public keys from keybase:

keybase pgp export --secret | gpg --allow-secret-key-import --import
keybase pgp export | gpg --import

Setup GPG Agent:

micro ~/.gnupg/gpg-agent.conf

With the below contents:

enable-ssh-support

default-cache-ttl-ssh 10800
max-cache-ttl-ssh 10800

pinentry-program /usr/bin/pinentry-gnome3

Setup PAM environment as extra:

micro ~/.pam-environment

With the below contents:

SSH_AGENT_PID	DEFAULT=
SSH_AUTH_SOCK	DEFAULT="${XDG_RUNTIME_DIR}/gnupg/S.gpg-agent.ssh"

Add your keys to SSH control file, that keeps the "authorized" keys to use by the emulated SSH agent:

ls ~/.gnupg/private-keys-v1.d/ | sed s/.key// >> ~/.gnupg/sshcontrol

Now logout and login again, or event better, reboot your machine. Check your SSH keys with:

ssh-add -l

If you are getting errors like error fetching identities: agent refused operation then you need to remove offending keys that are not Authorization keys. You can check them with:

gpg --list-keys --with-keygrip

Then comment out the keys with # or remove auth with ! at ~/.gnupg/sshcontrol:

micro ~/.gnupg/sshcontrol

Virtualization in Archlinux - Docker, Kubernetes, KVM

Tips and tricks for a ideal initial setup

Docker Setup

yay -S docker nvidia-container-toolkit
sudo usermod -a -G docker $(whoami)
sudo systemctl start docker
docker info

Kubernetes using K3s (K8s on K3s)

First you need to setup docker like above.

yay -S helm kubectl k3s-bin
set -gx KUBECONFIG /etc/rancher/k3s/k3s.yaml  #for fish shell
export KUBECONFIG=/etc/rancher/k3s/k3s.yaml   #for bash shell
sudo systemctl start k3s
sudo chown -R root.docker /etc/rancher/k3s
kubectl get pods --all-namespaces
helm ls --all-namespaces

Kubernetes Dashboard using fish shell

set GITHUB_URL https://github.com/kubernetes/dashboard/releases
set VERSION_KUBE_DASHBOARD (curl -w '%{url_effective}' -I -L -s -S ${GITHUB_URL}/latest -o /dev/null | sed -e 's|.*/||')
k3s kubectl create -f https://raw.githubusercontent.com/kubernetes/dashboard/${VERSION_KUBE_DASHBOARD}/aio/deploy/recommended.yaml

Now create a RBAC authentication for a single user:

Using mkdir -pv ~/Projects/k8s; micro ~/Projects/k8s/dashboard.admin-user.yml, paste this:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: admin-user
  namespace: kubernetes-dashboard

Using micro ~/Projects/k8s/dashboard.admin-user-role.yml, paste this:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: admin-user
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- kind: ServiceAccount
  name: admin-user
  namespace: kubernetes-dashboard

Now apply these new settings:

cd ~/Projects/k8s
k3s kubectl create -f dashboard.admin-user.yml -f dashboard.admin-user-role.yml

Accessing the dashboard

Obtain the auth token (Bearer token), copying to clipboard:

k3s kubectl -n kubernetes-dashboard describe secret admin-user-token | grep '^token' | sed -r 's/token:[[:blank:]]+//g' | xclip -sel clip

Create the proxy

k3s kubectl proxy

Now access: http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/

More info: https://rancher.com/docs/k3s/latest/en/installation/kube-dashboard/

Deleting the dashboard

k3s kubectl delete ns kubernetes-dashboard

KVM Setup on Arch

Basic installation

sudo pacman -S --needed qemu virt-manager virt-viewer dnsmasq vde2 bridge-utils openbsd-netcat ebtables iptables
sudo yay -S --needed libguestfs
sudo systemctl enable --now libvirtd.service

Enable normal user account to use KVM

sudo micro /etc/libvirt/libvirtd.conf

Uncomment these lines:

unix_sock_group = "libvirt"
unix_sock_rw_perms = "0770"

Back to shell:

sudo usermod -a -G libvirt $(whoami)
newgrp libvirt
sudo systemctl restart libvirtd.service

In my test we need more configuration to not get a laggy 2D experience, even on Manjaro KDE.

Julia

nteract notebooks plus IJulia Packages managed by pacman

It's a wonderful idea to delegate the package updates to the operating system.

ArchLinux AUR has many Julia packages already, including julia-ijulia for notebooks

yay -S julia-distrohelper julia-loadpath

Reboot or logout/login from your user, because these 2 packages manages the julia's loadpath using system packs as a fallback. Without that Julia envs will not recognize the system installed packages.

yay -S --needed julia-ijulia nteract-bin

Now you can install any packages starting with julia-* and notebook apps like nteract-bin will recognize Julia as a option to notebook environments.

Vivaldi Browser

Installation

yay -S --needed vivaldi

Proprietary codecs

You can install from AUR also, but they are slow to download and build (downloading an entire Google Chrome deb, etc)

yay -S --needed vivaldi-ffmpeg-codecs vivaldi-widevine vivaldi-codecs-ffmpeg-extra-bin

Or, below you can use the prebuilt binaries from Vivaldi to install in the user space.

Vivaldi Browser Proprietary Media on user space (no root)

The update-ffmpeg and update-widevine scripts included in the Vivaldi install directory are provided to fix situations where proprietary media (AVC/H.264 and AAC) and Widevine (DRM/EME) respectively, are not setup correctly.

These scripts are primarily intended to be run as root (or under sudo) as they create and update files and directories that are root owned. However both support a command line option (--user) that adjusts their installation directories and thus allows them to be run without escalation.

The --user options were made for internal usage, with locally ‘unpacked’ copies of Vivaldi (i.e. not installed). However, it is possible to use them with standard installs (albeit with a little tweaking in the case of update-widevine).

update-ffmpeg

To install proprietary media for just your current user issue the following:

/opt/vivaldi/update-ffmpeg --user

update-widevine

To install Widevine for just your current user, a couple more steps are required. This is because the script still attempts to adjust symlinks within the (typically root owned) Vivaldi install directory.

You can adjust the script as it runs, to disable the symlink creation, which would otherwise cause failure:

sed -r 's/^ *(rm|ln) -f.*/:/' /opt/vivaldi/update-widevine | sh -eus -- --user

Now create meta data in your Vivaldi user data directory that points to alternative Widevine install location–triple click to select the entire line:

echo "{\"Path\":\"$HOME/.local/lib/vivaldi/WidevineCdm\"}" > "${XDG_CONFIG_HOME:-$HOME/.config}/vivaldi/WidevineCdm/latest-component-updated-widevine-cdm"

Note: Change all “vivaldi” references in the above commands to “vivaldi-snapshot”, if you use the snapshot version.

(Original)[https://gist.github.com/ruario/995728dd8123540d331052e0ce648439]

Elixir language

List comprehensions

Filtering

animals = [ {"Liz", :dog}, {"Margot", :cat} ]

# pattern match
for {name, :cat} <- prefs, do: name
# ["Margot"]

# guard clause
for {name, pet_choice} <- animals, pet_choice == :dog, do: name
# ["Liz"]

# anonymous function in a guard clause
cat_lover? = fn(choice) -> choice == :cat end
for {name, pet_choice} <- prefs, cat_lover?.(pet_choice), do: name
# ["Margot"]

# destructuring plus filtering
pets = [
  %{name: "Fluffy", species: :cat, sleeping: true},
  %{name: "Joe", species: :dog, sleeping: false}
]
for pet <- pets,
    name = pet.name,
    sleeping = pet.sleeping do
  "Is #{name} sleeping? #{sleeping}"
end
# => ["Is Fluffy sleeping? true"]

# applying for each list
for i <- 1..2, IO.inspect(i), j <- 5..6 do
  {i, j}
end

for i <- 1..10, Integer.is_even(i), j <- 5..6 do
  {i, j}
end
# [{2, 5}, {2, 6}, {4, 5}, ...

Atomize keys - into

You can use into and do option from comprehensions to achieve this:

style = %{"color" => "#FFF", "display" => "flex", "border" => "2px"}
for {key, val} <- style, into: %{}, do: {String.to_atom(key), val}
# %{border: "2px", color: "#FFF", display: "flex"}

Cartesian product

names = ~w[John Jane]
surnames = ~w[Doe Silva]

for name <- names,
    surname <- surnames do
  "#{name} #{surname}"
end

Unique

for n <- [1, 1, 2, 2, 2, 3], uniq: true do
  n
end

Reduce

pets = [
  %{name: "Fluffy", species: :cat, sleeping: true},
  %{name: "Joe", species: :dog, sleeping: false},
  %{name: "Joe", species: :cat, sleeping: false}
]

for pet <- pets, reduce: %{} do
  acc ->
    Map.update(acc, pet.name, [pet], &[pet|&1])
end

# Groups by name, returns a new map
%{
  "Fluffy" => [%{name: "Fluffy", sleeping: true, species: :cat}],
  "Joe" => [
    %{name: "Joe", sleeping: false, species: :cat},
    %{name: "Joe", sleeping: false, species: :dog}
  ]
}

Filter, map at same time

for %{sleeping: false} = pet <- pets, into: %{} do
  {pet.name, pet}
end

# returns
%{"Joe" => %{name: "Joe", sleeping: false, species: :cat}}

Elixir async and state management

  • Use a Task if you want to perform a one-off computation or query asynchronously.
  • Use an Agent if you just need a simple process to keep the state.
  • Use a GenServer if you need a long-running server process that store states and performs work concurrently.
  • Use a dedicated GenServer process if you need to serialize access to a shared resource or service used by many concurrent processes.
  • Use a GenServer process if you need to schedule background work to run on a periodic interval.
  • You can start with one and migrate to another later, like from Agent to GenServer

GenServer callback functions

handle_call(message, from, state)

You want synchronous requests sent by the client. So the client is waiting for an immediate answer - like an ack answer - with the new state. from is usually ignored. It's format is {pid, tag}.

The return is typically {:reply, reply, new_state}.

handle_cast(message, state)

Handles asynchronous requests sent by the client. The client isn't waiting an acknowledgment.

The return is typically {:noreply, new_state}.

handle_info(message, state)

Handles all other requests. Useful to handle unknown messages as informational actions.

The return is typically {:noreply, state}.

init(args)

When GenServer.start(__MODULE__, [], name: @process_name) is called, [] is the initial state that will the init function will receive.

You can use to customize the initial state passed by the caller.

terminate(reason, state)

A handle_call or handle_cast function might return {:stop, reason, new_state}. terminate handles the process exit routines, like saving the state and closing files.

Debugging and Tracing

Check the current state

iex> :sys.get_state(pid)
%HttpServer.PledgeServer.State{cache_size: 5, pledges: [{"rodrigo", 15}, {"margot", 25}]}

Trace changes

iex> :sys.trace(pid, true)
:ok
iex> HttpServer.PledgeServer.create_pledge("moe", 20)

*DBG* pledge_server got call {create_pledge,<<"moe">>,20} from <0.152.0>

Full status

:sys.get_status(pid)

Phoenix Framework

LiveView

Powerful components

Render Slot is a powerful tool to render many similar cells inside a component

Extremely useful for lists, table rows, table cells, repeatable items that needs to be inside another, like with ul>li, tr>td, etc.

https://hexdocs.pm/phoenix_live_view/0.17.10/Phoenix.LiveView.Helpers.html#render_slot/2

<.unordered_list let={item} items={~w(Apples Oranges Bananas)}>
def unordered_list(assigns) do
  ~H"""
  <ul>
  <%= for item <- @items do %>
    <li><%= render_slot(@inner_block, item)%></li>
  <% end %>
  </ul>
  """
end

More on https://hexdocs.pm/phoenix_live_view/Phoenix.Component.html

Ruby

Summary

Frameworks

  • Rails

Ruby Struct

Structs: Simple Objects

Objective: Fancy hashes that responds to attributes names as a function, like in JS Objects

Car = Struct.new(:model, :maker, :year, keyword_init: true)

vw_up = Car.new model: 'Up!', year: 2015, maker: 'VW'
polo = Car.new('Polo', 'VW', 2018)
golf = Car.new 'Golf', 'VW'


polo.maker
# "VW"
golf.year
# nil

Equality: It uses their attributes instead object ids like default classes

# vw_up2 = Car.new model: 'Up!', maker: 'VW', year: 2015
vw_up == vw_up2
# True

OpenStruct: Simpler alternative

require 'ostruct'

cat = OpenStruct.new(color: 'black')
puts cat.class
puts cat.color

Differences from Struct

  • Struct creates a new class with predefined attributes, equality method (==) & enumerable
  • OpenStruct creates a new object with the given attributes

Double Dispatch

Objective: Turn a type checking imperative procedure in a more maintenable OO version.

Related to Visitor Pattern.

Some differences between the two on Refactoring Guru

Applications

  • Avoiding case for type checking in Ruby to comply with polymorphic collections
  • Use the Visitor when you need to perform an operation on all elements of a complex object structure (trasversing a tree). Like Visitor

One Example

Using a simple game, Rock, Paper, Scissors, like in RubyGuides we can start understand why:

class Scissors
    def wins_against?(other_move)
        case other_move
        when Rock then false
        when Paper then true
        end
    end
end

We want to avoid that example of case with this:

class Scissors
    def wins_against?(other_move)
        other_move.do_you_beat_scissors?
    end
    def do_you_beat_rock?
        false
    end
    def do_you_beat_paper?
        true
    end
end

Another example

# Hosts
class Car
    def dispatch_maintenance(caretaker)
      caretaker.maintenance_on_car(self)
    end
end

class Truck
    def dispatch_maintenance(caretaker)
      caretaker.maintenance_on_truck(self)
    end
end

# Visitors
class Mechanic
    def work_on(vehicle)
        vehicle.dispatch_maintenance(self)
    end
    def maintenance_on_car(vehicle)
        change_motor_oil(vehicle, tools: LightTools.grab())
    end
    def maintenance_on_truck(vehicle)
        change_motor_oil(vehicle, tools: HeavyTools.grab())
        check_tires_condition(vehicle) && change_tires(vehicle)
    end
end

class CarWasher
    def work_on(vehicle)
       vehicle.dispatch_work(self)
    end
    def maintenance_on_car(vehicle)
        grab_some_cleaning_cloth()
        do_wash(vehicle, using: LightMachine)
    end
    def maintenance_on_truck(vehicle)
        check_water_reservoir()
        do_wash(vehicle, using: HeavyMachine)
    end
end

mechanic = Mechanic.new
mechanic.work_on(vw_golf)

Here we see caretakers [Mechanic, CarWasher] visiting the patients instances [Car, Truck]. The hosts are responsible to dispatch the correct action for caretakers. It's odd on reality of this example.

Using a in View

Now from Sandi Metz's blog.

class MyView
  attr_reader :target
  
  def initialize(target)
    @target = target
  end

  def double
    case target
      when Numeric  then target * 2 
      when String   then target.next    # lazy example fail
      when Array    then target.collect {|o| MyView.new(o).double}
      else
        raise “don’t know how to double #{target.class} #{target.inspect}”
    end
  end
end
class Numeric
  def double
    self * 2
  end
end

class String
  def double
    self.next 
  end
end

class Array
  def double
    collect {|e| e.double}
  end
end

class Object
  def double
    raise "don't know how to double #{self.class} #{self.inspect}"
  end
end

class MyView
  attr_reader :target
  
  def initialize(target)
    @target = target
  end

  def double
    target.double
  end
end

Visitor

Objective: Separate Auxiliary procedures and algorithms from the object they operate.

We can consider the Visitor pattern a more complex version of double dispatch. It uses the double dispatch principle to make it work.

On top of double dispatch, Visitor lets you add “external” operations to a whole class hierarchy without changing the existing code of these classes.

Visitor isn’t a very common pattern because of its complexity and narrow applicability.

Related to Double Dispatch. Refactoring Guru

Applications

  • Use the Visitor when you need to perform an operation on all elements of a complex object structure (trasversing a tree).
  • Use the Visitor to clean up the business logic of auxiliary behaviors.

Example

class Node
  def accept visitor
    raise NotImpelementedError.new
  end
end

module Visitable
  def accept visitor
    visitor.visit(self)
  end
end

class IntegerNode < Node
  include Visitable

  attr_reader :value
  def initialize value
    @value = value
  end
end

class StringNode < Node
  include Visitable

  attr_reader :value
  def initialize value
    @value = value
  end
end

class Ast < Node
  def initialize
    @nodes = []
    @nodes << IntegerNode.new(2)
    @nodes << StringNode.new("3")
  end

  def accept visitor
    @nodes.each do |node|
      node.accept visitor
    end
  end
end

class BaseVisitor
  def visit subject
    method_name = "visit_#{subject.class}".intern
    send(method_name, subject )
  end
end

class DoublerVisitor < BaseVisitor
  def visit_IntegerNode subject
    puts subject.value * 2
  end

  def visit_StringNode subject
    puts subject.value.to_i * 2
  end
end

class TriplerVisitor < BaseVisitor
  def visit_IntegerNode subject
    puts subject.value * 3
  end

  def visit_StringNode subject
    puts subject.value.to_i * 3
  end
end

ast = Ast.new
puts "Doubler:"
ast.accept DoublerVisitor.new
puts "Tripler:"
ast.accept TriplerVisitor.new

# =>
Doubler:
4
6
Tripler:
6
9

Real World Usage

Arel uses this pattern to build specific database queries. See Arel PostgreSQL

Iterator

# It's just a class to demonstrate a collection capability that answers to enumerable idiom
class ACollection
  include Enumerable

  # @return [Array]
  attr_accessor :collection
  private :collection


  # @param [Array] collection
  def initialize(collection = [])
    @collection = collection
  end

  def each(&block)
    @collection.each(&block)
  end

  def <<(item)
    @collection << item
  end

  def append(item)
    self << item
  end
end


collection = ACollection.new
collection.append('First')
collection << 'Second'
collection.append('Third')

puts 'Straight traversal:'
collection.each { |item| puts item }
puts "\n"

puts 'Reverse traversal:'
collection.reverse_each { |item| puts item }

Python Tips

Install

ArchLinux or Manjaro system packages

For Data Science / Engineering

yay -S --needed python-ipykernel python-pandas python-numexpr python-beautifulsoup4 python-sqlalchemy python-xlrd python-xlwt python-tabulate python-psycopg2 python-pymysql python-seaborn python-statsmodels gnuplot python-numpy python-pandas-datareader python-scipy python-openpyxl python-matplotlib python-bottleneck mysql-python python-mysql-connector cython python-thrift python-fastparquet

For development

yay -S --needed python-flask python-django python-bcrypt python-coverage python-pytest-pylint python-pylint python-sqlalchemy

Python VirtualEnvs

VirtualEnvs on Fish Shell

First you need to use oh my fish, preferably with my .dotfiles

omf install virtualfish
pip install virtualfish
vf install compat_aliases projects environment auto_activation global_requirements update_python 

Then for existing projects:

vf new myprojectenv
vf connect
cd
vf disconnect
cd ~/Projects/myproject

Check if the environment auto changes.

Pipenv

pacman -S python-pipenv pyenv
omf install pyenv
pyenv install 3.7.7
pipenv

Then on any project folder:

pipenv install --python 3.7

Django

Using Django Shell Plus inside a VS Code Notebook

Preparing Django and the Python Virtual Env

Django Shell Plus is a nice way to explore your django project.

First on your project venv install the requisites:

pip install jupyterlab jupyter ipython django-extensions

Now add django-extensions to your DJANGO_PROJECT/settings.py:

INSTALLED_APPS = [
    'django_extensions',

Setting a fixed authentication

It's boring to copy and paste the token everytime you start the notebook to the VSCode, so we have two ways:

ps: disabling jupyter security does not work currently in vscode

With a Password

A nice trick is to set a password instead copying and pasting the jupyter token everytime you starts it, because jupyter changes it everytime you starts it.

Run this:

jupyter notebook password

To define a local password. This way VSCode will ask your password at least 4 times, but with a known value.

With a fixed token

Open ~/.jupyter/jupyter_notebook_config.json with your favorite editor then paste:

{
  "NotebookApp": {
    "token": "MY_SECURE_TOKEN"
  }
}

The real trick is here: you need to use a "remote" jupyter notebook, because the notebook started by VS Code is in a state that I cannot load Django successfully, even settings paths, messing with os.change_dir and similar workarounds.

Press CTRL+SHIFT+P in VSCode and type remote jupyter. When asked for the server address put this:

http://localhost:8888/?token=MY_SECURE_TOKEN

Now reload VSCode. Run in a separate terminal the external Django Shell Plus notebook, inside your project dir:

cd DJANGO_PROJECT

Then

python manage.py shell_plus --notebook --no-browser

Create a new notebook file anywhere inside your project, like in DJANGO_PROJECT/notebooks/test.ipynb.

Before running your first django code, run any notebook cell, it will connect to your "remote" jupyter server and ask for the password, unless you are using the fixed token.

Now FINALLY change the kernel to Django Shell Plus on the VSCode GUI, upper right. Only then you can import your models and start exploring your django data.

On your first cell put this:

import os
os.environ["DJANGO_ALLOW_ASYNC_UNSAFE"] = "true"
from myapp.models import Model1, Model2
Model1.objects.first()

Fluent Python

Chapter 9 on pytest

Chapter 9 of the Fluent Python book, by Luciano Ramalho, presents a Vector2d class.

It's a good reminder and example for experienced developers that are learning python. For instance, I can remember lots of concepts at once and compare with my other known programming languages, like Ruby.

Directory Structure suggestion

I also converted to the follow structure, recommended for a isolated component/module in python. I was used to Rails framework conventions with a different autoloader, so this reminds me that I need to create this structure by myself in python way.

And yes, as a seasoned Rubist, it's weird to put __init__ everywhere. Don't forget that.

+ fluentpy
|- __init__.py
|+ chapter9
 |- __init__.py
 |- vector2d.py
|+ tests
 |- __init__.py
 |+ chapter9
  |- __init__.py
  |- test_vector2d.py

Tests, converted to pytest

The original tests Ramalho presents on the book are the also widely used doctest. Buy the book and see there the original. The book is worthy every penny. I just converted (and added some bits) to pytest to make most of my learning and to remember in the future.

The coverage is 100% of the original code.

In the file fluentpy/tests/chapter9/test_vector2d.py:

import math
import pytest

import fluentpy.chapter9.vector2d as v2d

class TestVector2d:
    v1 = v2d.Vector2d(3, 4)

    def test_init_int(self):
        x, y = self.v1
        assert (x, y) == (3.0, 4.0), 'Must return a tuple'

    def test_clone_eval(self):
        v1_clone = eval('v2d.' + repr(self.v1))
        assert self.v1 == v1_clone, \
            'repr must be evaluated'

    def test_str(self):
        assert str(self.v1) == '(3.0, 4.0)', \
            'str conversion need to print a tuple'

    def test_bytes(self):
        assert bytes(self.v1) == b'd\x00\x00\x00\x00\x00\x00\x08@\x00\x00\x00\x00\x00\x00\x10@', \
            'byte representation must match'

    def test_abs(self):
        assert abs(self.v1) == 5.0, \
            'abs must be working'

    def test_bool(self):
        assert (bool(self.v1), bool(v2d.Vector2d(0, 0))) == (True, False), \
            'bool conversion must evaluate False on null vectors'

    def test_from_bytes(self):
        v1_clone = v2d.Vector2d.frombytes(bytes(self.v1))
        assert self.v1 == v1_clone

    def test_format(self):
        assert format(self.v1) == '(3.0, 4.0)', \
            'format without params'
        assert format(self.v1, '.2f') == '(3.00, 4.00)', \
            'format 2 decimal places'
        assert format(self.v1, '.3e') == '(3.000e+00, 4.000e+00)', \
            'format cientific exp mode'

    def test_angle(self):
        assert v2d.Vector2d(0, 0).angle() == 0.0, \
            'angle on null vector is zero'
        assert v2d.Vector2d(1, 0).angle() == 0.0, \
            'angle with y component zero is zero'
        epsilon = 10 ** -8
        assert abs(v2d.Vector2d(0, 1).angle() - math.pi / 2) < epsilon, \
            'angle must be valid on circle'
        # interesting reading: https://www.euclideanspace.com/maths/geometry/trig/inverse/index.htm
        assert abs(v2d.Vector2d(1, 1).angle() - math.pi / 4) < epsilon

    def test_format_polar(self):
        v_1_1 = v2d.Vector2d(1, 1)
        assert format(v_1_1, 'p') == '<1.4142135623730951, 0.7853981633974483>', \
            'vector in polar format must match'
        assert format(v_1_1, '.3ep') == '<1.414e+00, 7.854e-01>', \
            'vector in polar format, with 3 decimals and exponential rest must match'
        assert format(v_1_1, '0.5fp') == '<1.41421, 0.78540>', \
            'vector in polar format, with 5 decimals, truncated rest must match'

    def test_x_y_handling(self):
        # x and y must be read only
        with pytest.raises(AttributeError):  # as e_info:
            self.v1.x = 1
        with pytest.raises(AttributeError):
            self.v1.y = 2

    def test_hashing(self):
        v2 = v2d.Vector2d(3.1, 4.2)
        assert hash(self.v1) == 7, \
            'hash of vector (3, 4) is 7'
        assert hash(v2) == 384307168202284039, \
            'hash of a non integer components must work'
        assert hash(self.v1) != hash(v2), \
            'hash of (3, 4) must not match to (3.n, 4.n)'
        assert hash(self.v1) == hash(v2d.Vector2d(3, 4)), \
            'two different instance of vectors with same components must match'

    def test_set(self):
        assert len({self.v1, v2d.Vector2d(0, 0)}) == 2, \
            'converting to a set must work, with two different vectors'
        assert len({self.v1, self.v1}) == 1, \
            'converting to set must work, with two equivalent vectors'

The original code to be tested

File: fluentpy/chapter9/vector2d.py

from array import array
import math


class Vector2d:
    type_code = 'd'

    def __init__(self, x, y):
        self.__x = float(x)
        self.__y = float(y)

    @property  # getter
    def x(self):
        return self.__x

    @property  # getter
    def y(self):
        return self.__y

    def __iter__(self):
        return (i for i in (self.x, self.y))

    def __repr__(self):
        class_name = type(self).__name__
        return "{}({!r}, {!r})".format(class_name, *self)

    def __str__(self):
        return str(tuple(self))

    def __bytes__(self):
        return (bytes([ord(self.type_code)])) + \
               bytes(array(self.type_code, self))

    def __eq__(self, other):
        return tuple(self) == tuple(other)

    def __hash__(self):
        return hash(self.x) ^ hash(self.y)

    def __abs__(self):
        return math.hypot(self.x, self.y)

    def __bool__(self):
        return bool(abs(self))

    def angle(self):
        return math.atan2(self.y, self.x)

    def __format__(self, format_spec=''):
        if format_spec.endswith('p'):
            format_spec = format_spec[:-1]
            coords = (abs(self), self.angle())
            outer_format = '<{}, {}>'
        else:
            coords = self
            outer_format = '({}, {})'
        components = (format(c, format_spec) for c in coords)
        return outer_format.format(*components)

    @classmethod
    def frombytes(cls, octets):
        type_code = chr(octets[0])
        memv = memoryview(octets[1:]).cast(type_code)
        return cls(*memv)

In this code we have lots of methods that are used by common python functions. For instance str(something) converts the something to string. The something needs to respond to __str__, that will be called by str(). It's the equivalent in ruby to implement to_s in a class/module and use the something.to_s, that is a convetion to string conversions in Ruby.

Another example is __iter__ that must be implemented to make the object compatible with splat/unpack syntax *something or "matching" with x, y = something.

Every detail here is important for a reason. You will not use everything always.

Jupyter Notebook Tips

Notebook folder using remote VSCode

When you use a docker container with Jupyter Notebook and VSCode Remote Connect feature to develop your notebooks, you can have difficulties to load local python libraries inside you folder.

You need to set the correct working directory with this:

# Set the correct folder for Remote VSCode
import os

try:
    # '.' if the path is to current folder
    os.chdir(os.path.join(os.getcwd(), './notebooks'))
    # print(os.getcwd())
except:
    pass
os.getcwd()

Then you will have easier time to import your libs.

Helix Editor

Selection

:tutor Chapter 3 Recap

  • Type w to select forward until the next word.

    • Type e to select to the end of the current word.
    • Type b to select backward to the start of the current word.
    • Use uppercase counterparts, W,E,B, to traverse WORDS.
  • Type d to delete the entire selection.

    • Type c to delete the selection and enter Insert mode.
  • Type a number before a motion to repeat it that many times.

  • Type v to enter Select mode, where all motions extend the selection.

  • Type x to select the entire current line. Type x again to select the next line.

  • Type semicolon ( ; ) to collapse selection.

:tutor Chapter 4 Recap

  • Type u to undo. Type U to redo.

  • Type y to yank (copy) text and p to paste.

    • Use Space + y and Space + p to yank / paste on the system clipboard.
  • Type / to search forward in file, and ? to search backwards.

    • Use n and N to cycle through search matches.

Multi Cursor

:tutor Chapter 5 Recap

  • Type C to duplicate the cursor to the next suitable line and Alt-C for previous suitable line.

  • Type s to select all instances of a regex pattern inside the current selection.

  • Type & to align selections.

  • Type Alt-s to split the selection into lines.

Selection with Search / Replace

:tutor Chapter 6 Recap

  • Type f / F to extend selection up to & including a character.

    • Type t / T to extend selection until a character.
  • Type r to replace selected characters.

  • Type . to repeat the last insertion.

    • Type Alt-. to repeat the last f / t selection.

Advanced Manipulation

:tutor Chapter 7 Recap

  • Type R to replace the selection with yanked text.

  • Type J to join lines in selection.

  • Type < and > to indent / outdent lines.

  • Type Ctrl-a to increment the selected number.

    • Type Ctrl-x to decrement the selected number.

:tutor Chapter 9 Recap

  • Type * to set the search register to the primary selection.

  • Type n / N in Visual mode to add selections on each search match.

  • Type Ctrl-s to save position to the jumplist.

    • Type Ctrl-i and Ctrl-o to go forward and backward in the jumplist.

Manipulating Selections

:tutor Chapter 10 Recap

  • Use ) and ( to cycle the primary selection back and forward through selections respectively.

    • Type Alt-, to remove the primary selection.
  • Type ~ to alternate case of selected letters.

    • Use and Alt- to set the case of selected letters to upper and lower respectively.
  • Type S to split selections on regex.

Troubleshooting Gnome Alt+` backtick

You can set only Super+` for App switch.

 gsettings set org.gnome.desktop.wm.keybindings switch-group "['<Super>Above_Tab']"

Other Data Tools

More languages to play on Manjaro and a notebook

yay -S nteract-bin ihaskell-git r sagemath-jupyter julia

NVIDIA Accelerated Docker containers on ArchLinux

yay -S nvidia-container-runtime
sudo tee /etc/docker/daemon.json <<EOF
{
    "default-runtime": "nvidia",
    "runtimes": {
        "nvidia": {
            "path": "/usr/bin/nvidia-container-runtime",
            "runtimeArgs": []
        }
    }
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
docker run nvidia/cuda:10.1-base nvidia-smi

Compiling Psycopg2 for Lambda on ArchLinux

Please pay attention to the version numbers, change to newer versions as appropriate.

cd ~/Downloads
pacman -S postgresql postgresql-libs
wget http://initd.org/psycopg/tarballs/PSYCOPG-2-7/psycopg2-2.7.5.tar.gz
tar xf psycopg2-2.7.5.tar.gz
cd psycopg2-2.7.5/
sed -i 's/libpq.a/libpq.so/g' setup.py
gedit setup.cfg

Change setup.cfg build_ext section to that:

[build_ext]
define = 
pg_config = /usr/bin/pg_config
use_pydatetime = 1
mx_include_dir = 
have_ssl = 0
static_libpq = 1
libraries = ssl crypto

After that, we will finally build:

python setup.py build
cd build/lib.linux-x86_64-3.7
cp -R psycopg2/ ~/Projects/{MY_LAMBDA_PROJECT}/

Exporting PDF using jupyter nbconvert

yay -S texlive-most --needed

Redshift

Grant read only access to a group on a schema

-- Create Read-Only Group     
CREATE GROUP ro_group;

-- Create User
CREATE USER ro_user WITH password PASSWORD;

-- Add User to Read-Only Group
ALTER GROUP ro_group ADD USER ro_user;

-- Grant Usage permission to Read-Only Group to specific Schema
GRANT USAGE ON SCHEMA "ro_schema" TO GROUP ro_group;

-- Grant Select permission to Read-Only Group to specific Schema
GRANT SELECT ON ALL TABLES IN SCHEMA "ro_schema" TO GROUP ro_group;

-- Alter Default Privileges to maintain the permissions on new tables
ALTER DEFAULT PRIVILEGES IN SCHEMA "ro_schema" GRANT SELECT ON TABLES TO GROUP ro_group;

-- Revoke CREATE privileges from group
REVOKE CREATE ON SCHEMA "ro_schema" FROM GROUP ro_group;

Testing sorting keys

create table datasci.test_table_compound
compound sortkey (created_at, id)
as select * from public.test_table;

create table datasci.test_table_interleaved
interleaved sortkey (created_at, id)
as select * from public.test_table;

This copy for me over a million rows tables works pretty fast (< 1 minute), so it's worthy to copy some optimized tables.

MySQL

Number generator

The purpose is to join or count in special ocasions you can rely on the index, or use a custom order, on old versions without powerful window functions.

You can use maths + join on numbers to create discrete time windows based on created_at, for instance.

CREATE OR REPLACE VIEW generator_16
AS SELECT 0 n UNION ALL SELECT 1  UNION ALL SELECT 2  UNION ALL 
   SELECT 3   UNION ALL SELECT 4  UNION ALL SELECT 5  UNION ALL
   SELECT 6   UNION ALL SELECT 7  UNION ALL SELECT 8  UNION ALL
   SELECT 9   UNION ALL SELECT 10 UNION ALL SELECT 11 UNION ALL
   SELECT 12  UNION ALL SELECT 13 UNION ALL SELECT 14 UNION ALL 
   SELECT 15;

CREATE OR REPLACE VIEW generator_256
AS SELECT ( ( hi.n << 4 ) | lo.n ) AS n
     FROM generator_16 lo, generator_16 hi;

CREATE OR REPLACE VIEW generator_4k
AS SELECT ( ( hi.n << 8 ) | lo.n ) AS n
     FROM generator_256 lo, generator_16 hi;

CREATE OR REPLACE VIEW generator_64k
AS SELECT ( ( hi.n << 8 ) | lo.n ) AS n
     FROM generator_256 lo, generator_256 hi;

CREATE OR REPLACE VIEW generator_1m
AS SELECT ( ( hi.n << 16 ) | lo.n ) AS n
     FROM generator_64k lo, generator_16 hi;
-- create table for results

drop table if exists numbers ;

create table `numbers` (
  `i` int(11) signed 
  , primary key(`i`)
) ENGINE=myisam DEFAULT CHARSET=latin1;

INSERT INTO numbers(i)
SELECT n FROM generator_64k WHERE n < 64000

Creating database and privileges

mysql -h 127.0.0.1 -uroot -p
CREATE USER 'ghost'@'%';
GRANT ALL PRIVILEGES ON ghost.* To 'ghost'@'%' IDENTIFIED BY 'ghost123';
FLUSH PRIVILEGES;

CTRL-D to disconnect

OpenWRT tricks

TIM Live Brazil Fiber (FTTH)

It uses PPPoE like the VDSL, but you need to set the VLAN.

Unlike the VDSL connections, for FTTH connections you need the VLAN ID 100.

Unfortunately you can't use the Luci GUI interface. Tested with OpenWrt 21.02.

Edit /etc/config/network, specifically the find wan and wan6 interfaces:

config interface 'wan'
        option proto 'pppoe'
        option password 'guest'
        option ipv6 'auto'
        option username 'guest'
        option device 'eth0.2'
        option ifname 'dsl0.100'  ## This is essential

config interface 'wan6'
        option proto 'dhcpv6'
        option device 'eth0.2'

IPv6 needs more work, since my connection does not have it enabled, trying to enable inside the wan6 prevents me the connection with PADO timeouts.

Be aware that device 'eth0.2' can be different for your router model.

Slow PPPoE

If you're having slow throughput using PPPoE, specially under the FTTH connection above, enable Software flow offloading inside Firewall settings.

It will make QoS SQM to not work, but since I have plenty bandwith now, I just disabled it.

Blocking URLs at dnsmasq

uci add_list dhcp.@dnsmasq[0].address='/address.com/127.0.0.1'
uci commit dhcp
/etc/init.d/dnsmasq restart

Some useful packages

ca-bundle curl ddns-scripts ddns-scripts_cloudflare.com-v4 etherwake flent-tools ip-tiny iptables-mod-conntrack-extra iptables-mod-ipopt kmod-ifb kmod-ipt-conntrack-extra kmod-ipt-ipopt kmod-ipt-raw kmod-sched-cake kmod-sched-core kmod-udptunnel4 kmod-udptunnel6 kmod-wireguard libcap libcurl4 libelf1 libmbedtls12 libmnl0 libncurses6 libqrencode librt luci-app-ddns luci-app-sqm luci-app-wireguard luci-app-wol luci-compat luci-lib-ipkg luci-proto-wireguard nano netperf qrencode speedtest-netperf sqm-scripts tc terminfo wireguard wireguard-tools zlib

Ubuntu/Debian Based

Keybase

cd ~/Downloads
curl -O https://prerelease.keybase.io/keybase_amd64.deb
sudo dpkg -i keybase_amd64.deb
sudo apt-get install -f
run_keybase

Node / Yarn

curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash -
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list

sudo apt update
sudo apt install git-core curl zlib1g-dev build-essential libssl-dev libreadline-dev libyaml-dev libsqlite3-dev sqlite3 libxml2-dev libxslt1-dev libcurl4-openssl-dev software-properties-common libffi-dev nodejs yarn

Linux Brew

sudo apt install build-essential curl file git
sh -c "$(curl -fsSL https://raw.githubusercontent.com/Linuxbrew/install/master/install.sh)"
nano ~/.profile

test -d ~/.linuxbrew && eval $(~/.linuxbrew/bin/brew shellenv)
test -d /home/linuxbrew/.linuxbrew && eval $(/home/linuxbrew/.linuxbrew/bin/brew shellenv)
test -r ~/.bash_profile && echo "eval \$($(brew --prefix)/bin/brew shellenv)" >>~/.bash_profile
echo "eval \$($(brew --prefix)/bin/brew shellenv)" >>~/.profile

Configure a Ghost Blog at GCE

Node + Yarn + Requisites

DBUS and SETCAP are not installed by default on GCE Debian

curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash -

curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -

echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list

sudo apt-get update && sudo apt-get install -y yarn nodejs dbus libcap2-bin wget

Update Practices

sudo apt update && sudo apt upgrade

weekly

Ghost Engine

echo "PATH=$PATH:$HOME/.yarn/bin" >> ~/.bashrc
source ~/.bashrc
yarn global add ghost-cli@latest
sudo rm -rf /var/www/ghost
sudo mkdir -p /var/www/ghost
sudo chown $(whoami): /var/www/ghost

ghost install --url=https://MYSITE.COM --admin-url=https://ADMIN.MYSITE.COM --db=sqlite3 --mail=SMTP --mailservice=Mailgun [email protected] --mailpass=MAILGUN_SMTP_PW --no-stack --no-setup-ssl --no-prompt -d /var/www/ghost

Update Practices

ghost update

whenever a new version comes

Caddy Webserver

Auto HTTPS using Let's Encrypt

Caddyfile

First we need to create a Caddyfile, that is a configuration file for Caddy HTTPS Server. nano Caddyfile or vim Caddyfile and paste this:

MYSITE.COM {
  status 404 /ghost
  proxy / http://127.0.0.1:2368 {
    transparent
    fail_timeout 300s
    header_upstream X-Forwarded-Ssl on
  }

  tls [email protected]
  gzip
}

ADMIN.MYSITE.COM {
  status 404 //
  proxy / http://127.0.0.1:2368/ {
    transparent
    fail_timeout 300s
    header_upstream X-Forwarded-Ssl on
  }
  tls [email protected]
  gzip
}

www.MYSITE.COM {
  redir https://MYSITE.COM
  tls [email protected]
}

Obviously modify to your site settings.

I separated the admin site for hardening access. It has stricter rules at Cloudflare Edge. The ADMIN is some long string (kind of phrase password). It can make life harder for bots. Plus with stricter rules at my Cloudflare Edge for these bots (for instance, mandatory Captcha thoughter rate limits), it will not eat Network Fees from Google Cloud (or whatever you are using).

Installation

Now we will install and start the service:

curl https://getcaddy.com | bash -s personal
sudo setcap 'cap_net_bind_service=+ep' /usr/local/bin/caddy
sudo mkdir /etc/caddy
sudo chown -R root:www-data /etc/caddy
sudo mkdir /etc/ssl/caddy
sudo chown -R root:www-data /etc/ssl/caddy
sudo chmod 0770 /etc/ssl/caddy
sudo cp ~/Caddyfile /etc/caddy/
sudo chown www-data:www-data /etc/caddy/Caddyfile
sudo chmod 444 /etc/caddy/Caddyfile
sudo chown www-data:www-data /var/www
sudo chmod 555 /var/www
wget https://raw.githubusercontent.com/mholt/caddy/master/dist/init/linux-systemd/caddy.service
sudo cp caddy.service /etc/systemd/system/
sudo chown root:root /etc/systemd/system/caddy.service
sudo chmod 644 /etc/systemd/system/caddy.service
sudo systemctl daemon-reload
sudo systemctl start caddy.service
sudo systemctl enable caddy.service

Update practices

Whenever a new caddy version comes, or weekly:

curl https://getcaddy.com | bash -s personal
sudo pkill -USR2 caddy

Automating this is out of this doc scope (tip: CRON)

Logs

To follow connection logs on Caddy:

journalctl -f -u caddy.service

Or requests at node server:

journalctl -f -u ghost_MYSITE.service

Git

Redundancy

Add Multiple repositories

Refer to my dotfiles/fish/functions.fish file

Push to all remotes at same time

Creating an alias is a good idea

git config --global alias.pushall '!git remote | xargs -L1 git push --all'

Keychron with VIA inside Linux

It is supported only through Chromium

Add to /etc/udev/rules.d/50-keychron-chrome.rules

KERNEL=="hidraw*", ATTRS{idVendor}=="3434", MODE="0664", GROUP="plugdev"

Reboot your system to be sure, then go to https://usevia.app

Fedora does not have a plugdev group like ArchLinux, so change the group to something your user belongs.

WSL

Backup using WSL2 + Compression

Inside a Power Shell terminal:

wsl --export Distrod  - | zstd -10 -o "D:\archlinux-$(Get-Date -Format FileDateUniversal).tar"

You'll need zstd installed. Use scoop.sh inside PowerShell.

scoop install zstd

Podman

Running a container with Wireguard && Current User

set -lx UID (id -u)
set -lx GID (id -g)

podman run -d --user 0:0 --userns keep-id:uid=$UID,gid=$GID \
    --sysctl="net.ipv4.conf.all.src_valid_mark=1" \
    --privileged=true \

Wayland

Overriding electron apps with Wayland

This was tested using Fedora 37. The example below is using Logseq.

flatpak override --user --socket=wayland --env=GDK_BACKEND=wayland com.logseq.Logseq

Then try to open with:

GDK_BACKEND=wayland flatpak run com.logseq.Logseq --enable-features=UseOzonePlatform --ozone-platform=wayland

Now you can edit with Alacarte/Menu Editor:

Instead of

/usr/bin/flatpak run --branch=stable --arch=x86_64 --command=run.sh --file-forwarding com.logseq.Logseq @@u %U @@

Use this

/usr/bin/flatpak run --branch=stable --arch=x86_64 --command=run.sh --file-forwarding com.logseq.Logseq --enable-features=UseOzonePlatform --ozone-platform=wayland @@u %U @@

Obsidian

Obsidian has a dedicated environment variable:

flatpak override --user --env=OBSIDIAN_USE_WAYLAND=1 md.obsidian.Obsidian

Fedora Setup

Yubikey with GPG

This can help with Yubikey card not available after sleep.

Remove opensc

sudo dnf remove opensc

File ~/.gnupg/scdaemon.conf:

reader-port Yubico Yubi
pcsc-shared

File /etc/polkit-1/rules.d/10-pcsc-custom.rules:

polkit.addRule(function(action, subject) {
    if (action.id == "org.debian.pcsc-lite.access_pcsc" &&
        subject.user == "rdlu") {
            return polkit.Result.YES;
    }
});

polkit.addRule(function(action, subject) {
    if (action.id == "org.debian.pcsc-lite.access_card" &&
        action.lookup("reader") == 'Yubico YubiKey OTP+FIDO+CCID 00 00' &&
        subject.user == "rdlu") {
            return polkit.Result.YES;
    }
});