Zurückschauen, nach Vorne schauen

🪛

Im folgenden werden die Begriffe Lookahead- und Lookbehind-"Gruppen" benutzt.
Eigentlich heißen sie Lookahead-Assertions und Lookbehind-Assertions - ich benutze diesen Begriff wegen der Ähnlichkeit der Syntax zu Gruppen

Häufig will man gegen einen Ausdruck matchen, aber nur einen Teil des Ausdrucks eigentlich als Wert haben.
Dafür kann man Groups benutzen, benannte oder Anonyme.

result = re.findall("<(.*?)>", "Sometext <p><h2>Neuer Absatz</h2><b>Dicker text</b>")
... for i in result:
...     print(i)
p
h2
/h2
b
/b

Gematcht wird auf <.?> → also auf Zeichenketten die in <> stehen.
Durch die Gruppe → () → (.?) ist in der Ergebnismenge nur der Inhalt dieser, also der Text in den Tags enthalten.

Read more…

Gierige Regex und wo sie zu finden sind

Pythons Standard-Rgex-Library kennt greedy (gierige) und nicht-gierige reguläre Ausdrücke.
Dieser Artikel soll kurz darstellen unter welchen Umständen es dadurch zu Problemen kommen kann und wie man sie vermeidet.

Häufig ist einem gar nicht bewusst ob man einen gierigen oder nicht-gierigen regulären Ausdruck verwendet…
Man möchte zum Beispiel vereinfachtes HTML prfüen, sowas hier:

Read more…

Keyboard-Konfiguration unter X

Vorwort

Der folgende Artikel beschreibt wie man mehrere Tastaturkonfigurationen unter X konfiguriert.
In der Regel (in Desktopumgebungen) übernehmen das Dienste der Desktopumgebung, die dafür entsprechend auch grafische Werkzeuge mitbringen - in der Regel lässt sich dort aber nicht zwischen mehreren Tastaturen unterscheiden.

Da das Ganze ein paar Nachteile hat (und ich auch nicht das damit erreichen konnte was ich eigentlich wollte), hier ein paar Einschränkungen vorweg:

  • die Konfiguration gilt für alle Benutzer auf dem System

  • die Konfiguration kann zwar auf das einstecken einer Tastatur reagieren, aber nicht auf das Entfernen

Letzteres war eigentlich was ich wollte, das X die Tastaturkonfiguration ändert wenn man eine Tastatur steckt und sie auf eine andere ändert wenn man sie wieder entfernt.

Read more…

In guter Umgebung

Warum man /usr/bin/env als shebang nutzen sollte.

Einführung

Wenn man Shell-Scripte schreibt oder auch Python- oder Perl-Scripte so muss man immer den zu verwendenden Interpreter als sogenanntes Shebang in der ersten Zeile angeben:

#!/bin/python3

echo "Bla"

Die Zeile sagt der Shell die das Script ausführen soll mit welchem Interpreter das Script ausgeführt werden soll.
Dadurch kann man ein Script einfach durch Doppelklick in einem Dateiexplorer oder durch ./meinscript.py aufrufen.
Würde man sie nicht angeben müsste man ein entsprechendes Script mit dem entprechenden Interpreter aufrufen, also so zum Beispiel in der Kommandozeile:

python3 meinsrcipt.py

Schönheitsfehler

Leider hat das ganzei einen Schönheitsfehler wenn man das gleiche Script auf verschiedenen Systemen nutzen will oder mit anderen Menschen teilen.
Denn auf meinem Computer mag die bash unter /bin/bash liegen, bei anderen Installationen aber unter /usr/bin/bash oder überhaupt ganz woanders.
Bei anderen Interpretern gibt es ggf. noch mehr Vielfalt wo sie sich verstecken können.

"Lösung"

Auf allen (halbwegs modernen) Systemen gibt es das Programm env, welches in im Prinzip allen Fällen unter /usr/bin liegt.

#!/usr/bin/env python3

echo "Bla"

Wie man sieht wird der gewünschte Pfad an env übergeben und das findet heraus wo sich der gewünschte Interpreter befindet (solange er in einem Verzeichnis ist der in der PATH-Umgebungsvariable definiert ist).

C sei deine Sprache

Vor einiger Zeit wollte ich gerne dieses schöne Script https://github.com/joshpetit/rofi-mixer zur Audio-Steuerung in meinem Window-Manager QTile benutzen.
An sich soll da eine Auswahl kommen über welches Gerät man denn gerne Audio abspielen möchte, bei mir war die Liste leider leer.
Zu meinem Glück handelt es sich hier um Python-Code und das Programm ist auch nicht all zu groß, der relevante Code ist in etwa das was hier passiert:

Read more…

Es werde mehr Licht

💡

In Desktop-Umgebungen wie LXDE, Gnome, KDE, XFCE kann die Helligkeit in der Regel über einen entsprechenden Dienst der Umgebung, der über Applets und Tastenkürzel angesteuert werden kann, eingestellt werden.

Die Helligkeit von Bildschirmen (gemeint sind hier die von Laptops und ähnlichen Geräten) wird unter Linux durch die Datei /sys/class/backlight/<Gerätename>/brightness dargestellt.
Der konkrete Pfad hängt davon ab wie der entsprechende Kernel-Treiber das Gerät nennt, auf meinem aktuellen Laptop ist es zum Beispiel /sys/class/backlight/amdgpu_bl1/brightness .
Ließt man die Datei:

cat /sys/class/backlight/<Gerätename>/brightness
133

Read more…

Datalist das bessere selection?!

Selection-Lists sind schon eine nette Sache.
Für den Benutzer, er muss nicht erraten was zur Auswahl steht und ggf. wie es richtig zu schreiben ist damit die Anwendung es auch versteht.
Und für Ersteller, er braucht nicht aus der Benutzereingabe zu erraten welche der Optionen wohl die richtige sein könnte - validieren muss er das was er zurückbekommt natürlich trotzdem.
Also eigentlich alles in Butter… bis sagen wir die Liste nicht mehr aus 4 Arten von Eiscreme besteht, sondern lang wird… sehr lang…
Dann wird es nervend durch die Liste zu scrollen und den richtigen Eintrag zu suchen.
Leider gibt es in selection keine Möglichkeit zu suchen.

Und noch ein Szenario gibt es wo selection an die Grenzen stößt: Was wenn man dem Benutzer eine Liste mit Auswahl geben will, aber gleichzeitig die Option einen anderen Wert manuell einzugeben?

Datalist zur Rettung

Angehängt

Ich hatte die Notwendigkeit (habe ich dann letzen Endes anders/sauberer gelöst) in Flask an eine URL Parameter anzuhängen.

Szenario

Das Szenario war:

  • Link löst Get-Request aus, mit Parameter refer_to="https://whatever.com"

  • Route verarbeitet Get-Request und bettet refer_to als Form-Feld ein

  • Form wird abgesendet und generiert Post-Request mit Form-Feldern refer_to="https://whatever.com" und customer_id=5748

  • Route verarbeitet Post-Request und sendet redirect auf "https://whatever.com?customer_id=5748"

Ziel war also im letzten Schritt die URL und die custome_id zu verheiraten.

Normalerweise würde man url_for(Funktionsname, parameter) aufrufen und das würde dann für die Funktion Funktionsname die URL ermitteln und ggf. (wenn es in der Route keine Platzhalter für die Parameter gibt) die Parameter hinten anhängen.
In diesem Fall hatte ich aber schon eine URL, nicht den Namen einer Funktion/Methode.

Lösung

Tatsächlich war es tatsächlich weniger trivial als gedacht, weder Flask, noch Werkzeug boten irgendeine Möglichkeit das zu machen, urllib aus der Standardbibliothek konnte es theoretisch umständlich über das zerlegen der URL in ihre Teile und dann neu zusammenbauen…
Und natürlich könnte man es auch über String-cocanation machen, aber das ist auch nicht sauber und kann Probleme geben mit der Kodierung.

Letzten Endes habe ich eine Lösung bei Stackoverflow gefunden https://stackoverflow.com/questions/2506379/add-params-to-given-url-in-python:

1
2
3
4
5
6
7
from requests import PreparedRequest

raw_url="https://whatever.com"
customer_id = "5748"
fakerequest = PreparedRequest()
fakerequest.prepare_url(raw_redirect_url, {"customer_id": customer_id})
redirect_url = fakerequest.url
  • Zeile 4 erstellt pro forma einen PreparedRequest → einen Request den man zusammenbaut, aber erst später absenden kann

  • Zeile 5 baut die URL zusammen, aus der URL die wir schon haben + dem Parameter den wir anhängen wollen. Die Parameter werden als Dictionary als 2 Parameter von prepare_url übergeben

  • Zeile 6 holt sich die URL die der PreparedRequest nutzen würde aus dem Objekt → das ist die zusammengesetzte URL

Und schon hat man die URL wie man sie haben will.

Schreibgeschützt

Flask (und andere Frameworks) können WTForm nutzen um das erstellen von Formularen zu vereinfachen.

class InvoiceForm(FlaskForm):
    customer = StringField("Kunde")

Die Feld-Klassen (hier "StringField") haben in der Regel nur Attribute die man in der Regel (also das minimale Set) für dieses HTML-Element setzen wird - hier also den Namen, man könnte auch einen Anzeige-Wert setzen oder einen Default-Wert.
In meinem Fall fehlte mir das Attribut um ein Feld "readonly" zu machen.

class InvoiceForm(FlaskForm):
    customer = StringField("Kunde", render_kw={"readonly": "True"})

Für diese Fälle kann in jeder Feldklasse das Attribut "render_kw" gesetzt werden.
Dieses nimmt ein Ditctionary entgegen, der Key ist zu setzende HTML-Attribut und der Wert der Wert der diesem HTML-Attribut zugewiesen werden soll.

Das resultierende HTM sähe also so aus:

<input id="customer" name="customer" type="text" readonly="True"></input>

"render_kw" kann also benutzt werden um beliebige zusätzliche Attribute in einem Form-Element zu setzen.

class InvoiceForm(FlaskForm):
    customer = StringField("Kunde", render_kw={"readonly": "True", "whatever":"whoever"})

ergibt:

<input id="customer" name="customer" type="text" readonly="True" whatever="whoever"></input>

Das Setzen von readonly in render_kw wirkt sich nur in der Darstellung im Browser aus.
Diese Einschränkung lässt sich (im Browser oder in dem man keinen Browser nutzt um es zu senden) einfach umgehen und das Feld auf einen beliebigen Wert setzen.
Das das Feld nicht geändert wurde/werden kann muss auf Server-Seite (also in Flask usw) sichergestellt werden.
Flask reicht render_kw einfach durch, daraus resultieren keine Validatoren/Cheks! Das muss anderweitig sichergestellt werden.
Vertraue niemals Daten die von Außen kommen!

Die im Dunkeln sieht man nicht

Ich habe mir ein kleines Script geschrieben, was mir helfen soll Fehler die ich häufiger in AsciiDoc-Dokumenten mach schnell zu finden.
Vereinfacht gesagt wird dabei eine Datei Zeile für Zeile eingelesen und gegen eine Menge reguläre Ausdrücke geprüft.

U.a. muss erkant werden ob eine Zeile den Beginn eines Block enthält, ein Block wird so eingeleitet:

----

Der Code der darauf prüft sah dann entsprechend so aus:

import re

with open(filepath, "r", "utf-8") as fh:
	for line in fh:
		if re.fullmacht("----",line):
			do whatever

Read more…