Eine Frage des Kontexts

Einleitung

Vor kurzem hatte ich während der Entwicklung einer Flask-Anwendung folgende (gekürzte) Exception:

Traceback (most recent call last):
[…]
sqlalchemy.exc.InvalidRequestError: This session is provisioning a new connection; concurrent operations are not permitted (Background on this error at: https://sqlalche.me/e/20/isce)

SQLAlchemy beklagt sich darin darüber das ich versuche mehrere Verbindungen über die gleiche Session abzuwickeln.

Die relevanten Code-Stellen sehen so aus:

somesubmodule.py
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
from flask import render_template, Blueprint
from sqlalchemy import Session
from somedbmodule import engine

someblueprintp = Blueprint("somesubmodule", __name__)

sqlsession = Session(engine)

@someblueprintp.route("/list/", methods=["POST"])
def list_table():
    stmt = select(Customer)
    customers = sqlsession.scalars(stmt)
    return render_template("somefile.htm", customers=customers)

Weiterlesen…

Eigene Felder

Ziel

Für die Eingabe von Euro-Werten brauchte ich ein Form-Feld welches nur 2 Ziffern akzeptiert.

Der Nachteil eines normalen HTML-Input-Felds vom Typ "text" ist das Benutzer dort auch Text eingeben kann, und nicht definiert werden kann wie viel Nachkommastellen vorgesehen sind.
In HTML5 wurde ein Feld mit dem Input-Type "number" definiert, welches nur Zahlen als Wert akzeptiert - auf Mobilgeräten passt sich ggf. die Tastatur an und präsentiert nur Zahlen. Über das Attribut "step" lässt sich die Granularität der erlaubten Werte setzen.

step="0.01"

Weiterlesen…

Ersetzen von Tabelleninhalten via HTMX

Problem

Ich habe folgende Tabelle:

table filter marked

Wie man sehen kann gibt es (derzeit) einen Filter über den man definieren kann das man nur bezahlte Positionen sehen möchte.

Im Quellcode sieht das (auf das wesentliche gekürzt) so aus:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
<script src="https://cdn.jsdelivr.net/npm/htmx.org@2.0.8/dist/htmx.min.js"></script>
<table>
	<tr>
		<th>Datum</th>
		<th>Menge</th>
		<th>Einheit</th>
		<th>Bezeichnung</th>
		<th>Einzelpreis - Netto</th>
		<th>Gesamtpreis - Netto</th>
		<th>Mehrwertsteuer %</th>
		<th>Einzel - Mehrwertsteuer</th>
		<th>Einzelpreis - Brutto</th>
		<th>Gesamt - Mehrwertsteuer</th>
		<th>Gesamtpreis - Brutto</th>
		<th>Zahlungmethode</th>
		<th>Bezahlt</th>
		<th>Anmerkung</th>
		<th>Interne Anmerkung</th>
	</tr>
	<tr>
		<th></th>
		<th></th>
		<th></th>
		<th></th>
		<th></th>
		<th></th>
		<th></th>
		<th></th>
		<th></th>
		<th></th>
		<th></th>
		<th></th>
		<th>
			<input type="checkbox" name="payed" hx-trigger="click" hx-get="/deliveryposition/list/table" hx-target="#delivery-table" hx-swap="outerHTML" class="">
		</th>
		<th></th>
		<th></th>
	</tr>

	<tr>
		<td> 2026-01-03 </td>
		<td> 6 </td>
		<td> Stück </td>
		<td> Blubber </td>
		<td> 16.80672268907563 </td>
		<td> 100.84033613445379</td>
		<td> 19.0 </td>
		<td> 3.19327731092437 </td>
		<td> 20.0 </td>
		<td> 19.15966386554622 </td>
		<td> 120.0 </td>
		<td>  </td>
		<td> True </td>
		<td>  </td>
		<td>  </td>

</tr>
</table>

Weiterlesen…

DNS SRV-Records und Zertifikate

Das Problem

Für eine Erklärung wie srv-Einträge funktionieren und was das ist siehe hier: /posts/short_introduction_srv-records

_sip._tls.example.com. 3600 IN SRV 0 100 4866 sip1.example.com.

Bei SRV-Einträgen hat der Client nach der Auflösung des Namens mind. 2 Domain-Namen für einen Dienst.
Den ursprünglichen, den er konfiguriert bekommen hat/aus einer URI abgeleitet hat und aus dem er dann den SRV-Namen gebaut hat → example.com.
Den aus der Auflösung des SRV-Eintrags → sip.example.com.

Die Frage ist jetzt welchen erwartet er/muss er erwarten im Zertifikat zu sehen wenn er sich per TLS mit dem Dienst verbindet?

Weiterlesen…

Kurze Erklärung zu DNS SRV-Records

Der Artikel war eigentlich als Erklärung wie SRV-Rekords funktionieren für einen anderen Artikel geacht, ist aber dann so lang geworden, dass er jetzt ein eigener ist.

Eine Domain kann verschiedene Dienste anbieten, wie zum Beispiel Instant-Messaging (xmpp, Matrix), Telekommunikation (sip), Verzeichnisdiensten (LDAP) usw.
All diese Dienste verwenden ggf. die gleiche Domain, z.B. example.com, man hat dann also zum Beispiel eine JID: shellkraut@example.com (XMPP-Benutzername), eine SIP-URI: +4930124567@example.com usw.
Die Dienste teilen sich alle die gleiche Domain, aber können auf ganz verschiedenen Servern laufen, ggf. gibt es auch mehrere Server pro Dienst die nach einer bestimmten Priorisierung (Fallback) benutzt werden sollen und eventuell werden auch nicht die Standard-Ports der Dienste benutzt.
Man könnte auf Client-Seite natürlich neben der eigentlichen Benutzerkennung, auch die zu verwendendenden Server konfigurieren, also für sip zum Beispiel:

Weiterlesen…

Dataclass - TypeError: 'list' object is not callable

Gerade eben hatte ich ein Stacktrace in einem meiner Programme welches doch einen Moment gedauert hat bis ich herausgefunden hatte was das Problem war.

Sehr vereinfacht sah der Code wie folgt aus (in der Realität hatte das ganze mehr Felder und mehr Attribute und wurde natürlich auch aus einem anderen Modul heraus instanziert).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
rom dataclasses import dataclass, field
from typing import List


@dataclass
class MyTest:
    somelist: List = field(default_factory=list())


a = MyTest()

Der Fehler lautete dann:

Traceback (most recent call last):
  File "/tmp/mytest.py", line 10, in <module>
    a = MyTest()
  File "<string>", line 3, in __init__
TypeError: 'list' object is not callable

Und ich dachte: Was los? Ich rufe doch gar kein list-Objekt auf ("'list' object not callable"), was willst du eigentlich von mir…
Hier wird doch ganz klar nur eins instanziert in Zeile 7 und was hat das ganze mit Strings zu tun ("TypeError: 'list' object is not callable")?

Der Fehler ist in dieser Zeile:

    somelist: List = field(default_factory=list())

Eine default_factory muss eine Klasse sein, keine Instanz/Objekt, das Feld instanziert ein Objekt dieser Klasse wenn ihm in bei der Instanzierung der Data-Klasse (hier MyTest) kein Wert zugewiesen wird und weißt das Objekt der Variable als Default-Objekt zu.

Richtig muss es also

    somelist: List = field(default_factory=list)

heißen, also ohne () am Ende von list, weil kein Objekt erstellt wird, sondern eine Klasse übergeben.

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.

Weiterlesen…

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:

Weiterlesen…

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.

Weiterlesen…

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).