diff --git a/configs/baseline/profiledef.sh b/configs/baseline/profiledef.sh
index 40290a8..f0ba624 100644
--- a/configs/baseline/profiledef.sh
+++ b/configs/baseline/profiledef.sh
@@ -26,11 +26,4 @@ file_permissions=(
["/usr/local/bin/choose-mirror"]="0:0:755"
["/usr/local/bin/Installation_guide"]="0:0:755"
["/usr/local/bin/livecd-sound"]="0:0:755"
- ["/usr/local/bin/fws-pick-desktop"]="0:0:755"
- ["/usr/bin/fws-installer"]="0:0:755"
- ["/usr/local/bin/fws-desktop-init-common"]="0:0:755"
- ["/usr/local/bin/fws-desktop-init-xfce"]="0:0:755"
- ["/usr/local/bin/fws-desktop-init-kde"]="0:0:755"
- ["/usr/local/bin/fws-desktop-init-gnome"]="0:0:755"
- ["/usr/local/bin/fws-desktop-init-hyprland"]="0:0:755"
)
diff --git a/pkgbuilds/python-meh/PKGBUILD b/pkgbuilds/python-meh/PKGBUILD
new file mode 100644
index 0000000..7d565ed
--- /dev/null
+++ b/pkgbuilds/python-meh/PKGBUILD
@@ -0,0 +1,29 @@
+# Maintainer: FWS
+# Dépendance de l'installateur Anaconda (rhinstaller) — gestion d'exceptions.
+# setup.py amont = distutils + `setup.py install` : tous deux supprimés
+# (Python 3.12+, setuptools 80+). Le paquet étant du pur Python, on installe
+# l'arborescence à la main plutôt que de patcher la chaîne distutils.
+pkgname=python-meh
+pkgver=0.43
+pkgrel=1
+pkgdesc="Python exception handling library (dépendance Anaconda)"
+arch=('any')
+url="https://github.com/rhinstaller/python-meh"
+license=('GPL-2.0-or-later')
+depends=('python' 'python-gobject' 'python-dbus' 'libreport')
+_tag="r$pkgver-1"
+source=("$pkgname-$pkgver.tar.gz::https://github.com/rhinstaller/python-meh/archive/refs/tags/$_tag.tar.gz")
+sha256sums=('cd50301794ab68a064715f1ace960f6de07aea809aa85f73087d44573c98be52')
+_srcdir="python-meh-$_tag"
+
+package() {
+ cd "$srcdir/$_srcdir"
+ local sitedir
+ sitedir=$(python -c 'import site; print(site.getsitepackages()[0])')
+ install -d "$pkgdir$sitedir"
+ cp -a meh "$pkgdir$sitedir/meh"
+ # -d : grave le chemin d'exécution réel dans les .pyc (pas le $pkgdir de build).
+ python -m compileall -q -d "$sitedir/meh" "$pkgdir$sitedir/meh" || true
+ install -Dm644 ui/exception-dialog.glade -t "$pkgdir/usr/share/python-meh/"
+ install -Dm644 COPYING "$pkgdir/usr/share/licenses/$pkgname/COPYING"
+}
diff --git a/pkgbuilds/python-meh/pkg/python-meh/.BUILDINFO b/pkgbuilds/python-meh/pkg/python-meh/.BUILDINFO
new file mode 100644
index 0000000..ec09f92
--- /dev/null
+++ b/pkgbuilds/python-meh/pkg/python-meh/.BUILDINFO
@@ -0,0 +1,898 @@
+format = 2
+pkgname = python-meh
+pkgbase = python-meh
+pkgver = 0.43-1
+pkgarch = any
+pkgbuild_sha256sum = 207bd70c0f151083b8194b40534aeed665c9af48e588230ad370757eb4368229
+packager = Unknown Packager
+builddate = 1781307281
+builddir = /home/nocode/Files/FWS-ISO/pkgbuilds/python-meh
+startdir = /home/nocode/Files/FWS-ISO/pkgbuilds/python-meh
+buildtool = makepkg
+buildtoolver = 7.1.0
+buildenv = !distcc
+buildenv = color
+buildenv = !ccache
+buildenv = check
+buildenv = !sign
+options = strip
+options = docs
+options = !libtool
+options = !staticlibs
+options = emptydirs
+options = zipman
+options = purge
+options = debug
+options = lto
+installed = aardvark-dns-1.17.1-1-x86_64
+installed = abseil-cpp-20260107.1-1-x86_64
+installed = accountsservice-26.13.3-1-x86_64
+installed = acl-2.3.2-2-x86_64
+installed = adwaita-cursors-50.0-1-any
+installed = adwaita-fonts-50.0-1-any
+installed = adwaita-icon-theme-50.0-1-any
+installed = adwaita-icon-theme-legacy-46.2-3-any
+installed = alsa-card-profiles-1:1.6.6-1-x86_64
+installed = alsa-lib-1.2.16-1-x86_64
+installed = alsa-plugins-1:1.2.12-5-x86_64
+installed = alsa-topology-conf-1.2.5.1-4-any
+installed = alsa-ucm-conf-1.2.16-2-any
+installed = aom-3.14.1-1-x86_64
+installed = appstream-1.1.2-1-x86_64
+installed = aquamarine-0.12.1-1-x86_64
+installed = arch-install-scripts-31-1-any
+installed = archiso-88-1-any
+installed = archlinux-keyring-20260420-1-any
+installed = argon2-20190702-6-x86_64
+installed = at-spi2-core-2.60.4-1-x86_64
+installed = atkmm-2.28.5-2-x86_64
+installed = attr-2.5.2-2-x86_64
+installed = audit-4.1.4-2-x86_64
+installed = autoconf-2.73-1-any
+installed = automake-1.18.1-1-any
+installed = avahi-1:0.9rc4-1-x86_64
+installed = base-3-3-any
+installed = base-devel-1-2-any
+installed = bash-5.3.15-1-x86_64
+installed = bemoji-0.4.0-1-any
+installed = binutils-2.46+r70+g155188ea10a7-1-x86_64
+installed = bison-3.8.2-8-x86_64
+installed = blas-3.12.1-2-x86_64
+installed = bluez-libs-5.86-6-x86_64
+installed = boost-libs-1.91.0-1-x86_64
+installed = brightnessctl-0.5.1-3-x86_64
+installed = brotli-1.2.0-1-x86_64
+installed = bubblewrap-0.11.2-1-x86_64
+installed = bzip2-1.0.8-6-x86_64
+installed = c-ares-1.34.6-1-x86_64
+installed = ca-certificates-20240618-1-any
+installed = ca-certificates-mozilla-3.124-1-x86_64
+installed = ca-certificates-utils-20240618-1-any
+installed = cabextract-1.11-2-x86_64
+installed = cairo-1.18.4-1-x86_64
+installed = cairomm-1.14.6-1-x86_64
+installed = cairomm-1.16-1.18.1-1-x86_64
+installed = catatonit-0.2.1-2-x86_64
+installed = cblas-3.12.1-2-x86_64
+installed = cliphist-1:0.7.0-2-x86_64
+installed = clucene-2.3.3.4-17-x86_64
+installed = cmake-4.3.3-1-x86_64
+installed = compiler-rt-22.1.6-1-x86_64
+installed = composefs-1.0.8-1-x86_64
+installed = conmon-1:2.2.1-1-x86_64
+installed = containers-common-1:0.67.1-3-any
+installed = coreutils-9.11-1-x86_64
+installed = cpio-2.15-3-x86_64
+installed = cppdap-1.58.0-3-x86_64
+installed = cracklib-2.10.3-1-x86_64
+installed = criu-4.2-3-x86_64
+installed = crun-1.28-1-x86_64
+installed = cryptsetup-2.8.6-1-x86_64
+installed = curl-8.20.0-7-x86_64
+installed = dav1d-1.5.3-1-x86_64
+installed = db5.3-5.3.28-7-x86_64
+installed = dbus-1.16.2-1-x86_64
+installed = dbus-broker-37-3-x86_64
+installed = dbus-broker-units-37-3-x86_64
+installed = dbus-units-37-3-x86_64
+installed = dconf-0.49.0-1-x86_64
+installed = debugedit-5.3-1-x86_64
+installed = default-cursors-3-1-any
+installed = desktop-file-utils-0.28-1-x86_64
+installed = device-mapper-2.03.41-1-x86_64
+installed = diffutils-3.12-2-x86_64
+installed = discord-1:1.0.142-1-x86_64
+installed = dkms-3.4.1-1-any
+installed = dos2unix-7.5.6-1-x86_64
+installed = dosfstools-4.2-5-x86_64
+installed = double-conversion-3.4.0-1-x86_64
+installed = duktape-2.7.0-7-x86_64
+installed = e2fsprogs-1.47.4-1-x86_64
+installed = efibootmgr-18-4-x86_64
+installed = efivar-39-2-x86_64
+installed = egl-gbm-1.1.3-1-x86_64
+installed = egl-wayland-4:1.1.21-1-x86_64
+installed = egl-wayland2-1.0.1-1-x86_64
+installed = egl-x11-1.0.5-1-x86_64
+installed = eglexternalplatform-1.2.1-1-any
+installed = electron41-41.7.2-1-x86_64
+installed = elfutils-0.195-1-x86_64
+installed = erofs-utils-1.9.1-1-x86_64
+installed = exo-4.20.0-2-x86_64
+installed = expat-2.8.1-1-x86_64
+installed = extra-cmake-modules-6.26.0-1-any
+installed = fakeroot-1:1.37.2-1-x86_64
+installed = fcft-3.3.3-1-x86_64
+installed = ffmpeg-2:8.1.1-2-x86_64
+installed = fftw-3.3.11-1-x86_64
+installed = file-5.48-1-x86_64
+installed = filesystem-2025.10.12-1-any
+installed = findutils-4.10.0-3-x86_64
+installed = firefox-151.0.4-1-x86_64
+installed = flac-1.5.0-1-x86_64
+installed = flatpak-1:1.18.0-1-x86_64
+installed = flex-2.6.4-6-x86_64
+installed = fmt-12.1.0-2-x86_64
+installed = fontconfig-2:2.18.1-1-x86_64
+installed = freerdp-2:3.26.0-1-x86_64
+installed = freetype2-2.14.3-1-x86_64
+installed = fribidi-1.0.16-2-x86_64
+installed = fuse-common-3.18.2-1-x86_64
+installed = fuse2-2.9.9-5-x86_64
+installed = fuse3-3.18.2-1-x86_64
+installed = fuzzel-1.14.1-1-x86_64
+installed = gawk-5.4.0-1-x86_64
+installed = gc-8.2.12-1-x86_64
+installed = gcc-16.1.1+r12+g301eb08fa2c5-1-x86_64
+installed = gcc-libs-16.1.1+r12+g301eb08fa2c5-1-x86_64
+installed = gcr-3.41.2-2-x86_64
+installed = gcr-4-4.4.0.1-1-x86_64
+installed = gdb-17.2-1-x86_64
+installed = gdb-common-17.2-1-x86_64
+installed = gdbm-1.26-2-x86_64
+installed = gdk-pixbuf2-2.44.6-2-x86_64
+installed = gettext-1.0-2-x86_64
+installed = giflib-6.1.3-1-x86_64
+installed = git-2.54.0-1-x86_64
+installed = glib-networking-1:2.80.1-1-x86_64
+installed = glib2-2.88.1-1-x86_64
+installed = glibc-2.43+r22+g8362e8ce10b2-2-x86_64
+installed = glibmm-2.66.8-2-x86_64
+installed = glibmm-2.68-2.88.0-2-x86_64
+installed = glslang-1:1.4.350.0-1-x86_64
+installed = glycin-2.1.1-1-x86_64
+installed = gmp-6.3.0-3-x86_64
+installed = gnome-themes-extra-1:3.28-1-any
+installed = gnulib-l10n-20241231-1-any
+installed = gnupg-2.4.9-1-x86_64
+installed = gnutls-3.8.13-2-x86_64
+installed = go-2:1.26.4-1-x86_64
+installed = gobject-introspection-1.86.0-2-x86_64
+installed = gobject-introspection-runtime-1.86.0-2-x86_64
+installed = gparted-1.8.1-2-x86_64
+installed = gperftools-2.18.1-1-x86_64
+installed = gpgme-2.1.0-1-x86_64
+installed = gpgmepp-2.1.0-1-x86_64
+installed = gpm-1.20.7.r38.ge82d1a6-6-x86_64
+installed = gpsd-3.27.5-1-x86_64
+installed = gptfdisk-1.0.10-2-x86_64
+installed = graphene-1.10.8-2-x86_64
+installed = graphite-1:1.3.15-1-x86_64
+installed = grep-3.12-2-x86_64
+installed = grim-1.5.0-2-x86_64
+installed = groff-1.24.1-1-x86_64
+installed = grub-2:2.14-1-x86_64
+installed = gsettings-desktop-schemas-50.1-1-any
+installed = gsettings-system-schemas-50.1-1-any
+installed = gsm-1.0.24-1-x86_64
+installed = gssdp-1.6.5-1-x86_64
+installed = gst-plugins-bad-libs-1.28.3-1-x86_64
+installed = gst-plugins-base-libs-1.28.3-1-x86_64
+installed = gstreamer-1.28.3-1-x86_64
+installed = gtest-1.17.0-2-x86_64
+installed = gtk-layer-shell-0.10.1-1-x86_64
+installed = gtk-update-icon-cache-1:4.22.4-1-x86_64
+installed = gtk3-1:3.24.52-1-x86_64
+installed = gtk4-1:4.22.4-1-x86_64
+installed = gtkmm-4.0-4.22.0-2-x86_64
+installed = gtkmm3-3.24.11-1-x86_64
+installed = guile-3.0.11-1-x86_64
+installed = gupnp-1:1.6.10-1-x86_64
+installed = gupnp-igd-1.6.0-2-x86_64
+installed = gzip-1.14-2-x86_64
+installed = harfbuzz-14.2.1-1-x86_64
+installed = harfbuzz-icu-14.2.1-1-x86_64
+installed = hicolor-icon-theme-0.18-1-any
+installed = hidapi-0.15.0-1-x86_64
+installed = highway-1.4.0-1-x86_64
+installed = hunspell-1.7.3-1-x86_64
+installed = hwdata-0.408-1-any
+installed = hwloc-2.13.0-1-x86_64
+installed = hyphen-2.8.9-1-x86_64
+installed = hyprcursor-0.1.13-6-x86_64
+installed = hyprgraphics-0.5.1-2-x86_64
+installed = hypridle-0.1.7-9-x86_64
+installed = hyprland-0.55.4-1-x86_64
+installed = hyprland-guiutils-0.2.1-6-x86_64
+installed = hyprland-qt-support-0.1.0-12-x86_64
+installed = hyprlang-0.6.8-4-x86_64
+installed = hyprlock-0.9.5-4-x86_64
+installed = hyprpaper-0.8.4-2-x86_64
+installed = hyprpicker-0.4.7-1-x86_64
+installed = hyprpolkitagent-0.1.3-7-x86_64
+installed = hyprshot-1.3.0-4-any
+installed = hyprtoolkit-0.5.4-2-x86_64
+installed = hyprutils-0.13.1-1-x86_64
+installed = hyprwayland-scanner-0.4.6-1-x86_64
+installed = hyprwire-0.3.1-2-x86_64
+installed = iana-etc-20260530-1-any
+installed = icu-78.3-1-x86_64
+installed = imagemagick-7.1.2.25-1-x86_64
+installed = iniparser-4.2.6-2-x86_64
+installed = iproute2-7.0.0-1-x86_64
+installed = iptables-1:1.8.13-1-x86_64
+installed = iputils-20250605-1-x86_64
+installed = iso-codes-4.20.1-1-any
+installed = jack2-1.9.22-2-x86_64
+installed = jansson-2.15.0-1-x86_64
+installed = jbigkit-2.1-8-x86_64
+installed = jq-1.8.1-3-x86_64
+installed = json-c-0.18-2-x86_64
+installed = json-glib-1.10.8-1-x86_64
+installed = jsoncpp-1.9.6-3-x86_64
+installed = kbd-2.10.0-1-x86_64
+installed = kcoreaddons-6.26.0-1-x86_64
+installed = keyutils-1.6.3-4-x86_64
+installed = ki18n-6.26.0-1-x86_64
+installed = kitty-0.47.1-1-x86_64
+installed = kitty-shell-integration-0.47.1-1-x86_64
+installed = kitty-terminfo-0.47.1-1-x86_64
+installed = kmod-34.2-1-x86_64
+installed = kpmcore-26.04.2-1-x86_64
+installed = krb5-1.22.2-1-x86_64
+installed = kwidgetsaddons-6.26.0-1-x86_64
+installed = l-smash-2.14.5-4-x86_64
+installed = lame-3.101.r6531-1-x86_64
+installed = lapack-3.12.1-2-x86_64
+installed = lcms2-2.19.1-1-x86_64
+installed = leancrypto-1.7.2-1-x86_64
+installed = lib32-alsa-lib-1.2.16-1-x86_64
+installed = lib32-alsa-plugins-1.2.12-1-x86_64
+installed = lib32-brotli-1.1.0-1-x86_64
+installed = lib32-bzip2-1.0.8-4-x86_64
+installed = lib32-curl-8.20.0-7-x86_64
+installed = lib32-dbus-1.16.2-1-x86_64
+installed = lib32-e2fsprogs-1.47.4-1-x86_64
+installed = lib32-expat-2.8.1-1-x86_64
+installed = lib32-fontconfig-2:2.18.1-1-x86_64
+installed = lib32-freetype2-2.14.3-1-x86_64
+installed = lib32-gcc-libs-16.1.1+r12+g301eb08fa2c5-1-x86_64
+installed = lib32-glib2-2.88.1-1-x86_64
+installed = lib32-glibc-2.43+r22+g8362e8ce10b2-2-x86_64
+installed = lib32-gmp-6.3.0-2-x86_64
+installed = lib32-gnutls-3.8.13-3-x86_64
+installed = lib32-icu-78.3-1-x86_64
+installed = lib32-json-c-0.18-2-x86_64
+installed = lib32-keyutils-1.6.3-4-x86_64
+installed = lib32-krb5-1.22.2-1-x86_64
+installed = lib32-libdrm-2.4.134-1-x86_64
+installed = lib32-libelf-0.195-1-x86_64
+installed = lib32-libffi-3.5.2-1-x86_64
+installed = lib32-libgcrypt-1.12.2-1-x86_64
+installed = lib32-libglvnd-1.7.0-1-x86_64
+installed = lib32-libgpg-error-1.61-1-x86_64
+installed = lib32-libidn2-2.3.8-1-x86_64
+installed = lib32-libldap-2.6.13-1-x86_64
+installed = lib32-libnghttp2-1.69.0-1-x86_64
+installed = lib32-libnghttp3-1.16.0-1-x86_64
+installed = lib32-libngtcp2-1.23.0-1-x86_64
+installed = lib32-libnm-1.56.1-1-x86_64
+installed = lib32-libpciaccess-0.19-1-x86_64
+installed = lib32-libpipewire-1:1.6.6-1-x86_64
+installed = lib32-libpng-1.6.58-1-x86_64
+installed = lib32-libpsl-0.21.5-1-x86_64
+installed = lib32-libssh2-1.11.1-1-x86_64
+installed = lib32-libtasn1-4.21.0-1-x86_64
+installed = lib32-libunistring-1.4.2-1-x86_64
+installed = lib32-libva-2.22.0-1-x86_64
+installed = lib32-libx11-1.8.13-1-x86_64
+installed = lib32-libxau-1.0.12-1-x86_64
+installed = lib32-libxcb-1.17.0-1-x86_64
+installed = lib32-libxcrypt-4.5.2-1-x86_64
+installed = lib32-libxcrypt-compat-4.5.2-1-x86_64
+installed = lib32-libxdmcp-1.1.5-1-x86_64
+installed = lib32-libxext-1.3.7-1-x86_64
+installed = lib32-libxfixes-6.0.1-2-x86_64
+installed = lib32-libxinerama-1.1.6-1-x86_64
+installed = lib32-libxml2-2.15.3-1-x86_64
+installed = lib32-libxshmfence-1.3.3-1-x86_64
+installed = lib32-libxss-1.2.5-1-x86_64
+installed = lib32-libxxf86vm-1.1.5-2-x86_64
+installed = lib32-llvm-libs-1:22.1.6-1-x86_64
+installed = lib32-lm_sensors-1:3.6.2-2-x86_64
+installed = lib32-mesa-1:26.1.2-1-x86_64
+installed = lib32-ncurses-6.6-2-x86_64
+installed = lib32-nettle-4.0-2-x86_64
+installed = lib32-nspr-4.39-1-x86_64
+installed = lib32-nss-3.124-1-x86_64
+installed = lib32-nvidia-utils-610.43.02-1-x86_64
+installed = lib32-openssl-1:3.6.3-1-x86_64
+installed = lib32-p11-kit-0.26.2-1-x86_64
+installed = lib32-pcre2-10.47-1-x86_64
+installed = lib32-pipewire-1:1.6.6-1-x86_64
+installed = lib32-spirv-tools-1:1.4.350.0-1-x86_64
+installed = lib32-sqlite-3.53.2-1-x86_64
+installed = lib32-systemd-260.2-1-x86_64
+installed = lib32-util-linux-2.42.1-1-x86_64
+installed = lib32-vulkan-icd-loader-1.4.350.0-1-x86_64
+installed = lib32-wayland-1.25.0-1-x86_64
+installed = lib32-xz-5.8.3-1-x86_64
+installed = lib32-zlib-1.3.2-1-x86_64
+installed = lib32-zstd-1.5.7-2-x86_64
+installed = libabw-0.1.4-1-x86_64
+installed = libadwaita-1:1.9.1-1-x86_64
+installed = libaio-0.3.113-4-x86_64
+installed = libappindicator-12.10.1-1-x86_64
+installed = libarchive-3.8.7-1-x86_64
+installed = libasan-16.1.1+r12+g301eb08fa2c5-1-x86_64
+installed = libass-0.17.4-2-x86_64
+installed = libassuan-3.0.0-1-x86_64
+installed = libasyncns-1:0.8+r3+g68cd5af-3-x86_64
+installed = libatomic-16.1.1+r12+g301eb08fa2c5-1-x86_64
+installed = libatomic_ops-7.8.2-2-x86_64
+installed = libavc1394-0.5.4-7-x86_64
+installed = libavif-1.4.2-1-x86_64
+installed = libb2-0.98.1-3-x86_64
+installed = libblake3-1.8.4-1-x86_64
+installed = libbluray-1.4.1-1-x86_64
+installed = libbpf-1.7.0-1-x86_64
+installed = libbs2b-3.1.0-10-x86_64
+installed = libbsd-0.12.2-2-x86_64
+installed = libburn-1.5.8-1-x86_64
+installed = libcanberra-1:0.30+r2+gc0620e4-6-x86_64
+installed = libcap-2.78-1-x86_64
+installed = libcap-ng-0.9.3-1-x86_64
+installed = libcbor-0.14.0-1-x86_64
+installed = libcdio-2.3.0-1-x86_64
+installed = libcdio-paranoia-10.2+2.0.2-2-x86_64
+installed = libcdr-0.1.9-1-x86_64
+installed = libcloudproviders-0.4.0-1-x86_64
+installed = libcmis-0.6.3-1-x86_64
+installed = libcolord-1.4.8-1-x86_64
+installed = libcups-2:2.4.19-1-x86_64
+installed = libdaemon-0.14-6-x86_64
+installed = libdatrie-0.2.14-1-x86_64
+installed = libdbusmenu-glib-18.10.20180917-1-x86_64
+installed = libdbusmenu-gtk3-18.10.20180917-1-x86_64
+installed = libdecor-0.2.5-1-x86_64
+installed = libdeflate-1.25-1-x86_64
+installed = libdisplay-info-0.3.0-1-x86_64
+installed = libdovi-3.3.2-1-x86_64
+installed = libdrm-2.4.134-1-x86_64
+installed = libdvdnav-7.0.0-1-x86_64
+installed = libdvdread-7.0.1-1-x86_64
+installed = libe-book-0.1.3-20-x86_64
+installed = libebur128-1.2.6-2-x86_64
+installed = libedit-20260512_3.1-1-x86_64
+installed = libei-1.6.0-1-x86_64
+installed = libelf-0.195-1-x86_64
+installed = libepoxy-1.5.10-3-x86_64
+installed = libepubgen-0.1.1-6-x86_64
+installed = libetonyek-0.1.13-2-x86_64
+installed = libevdev-1.13.6-1-x86_64
+installed = libevent-2.1.12-5-x86_64
+installed = libexif-0.6.26-1-x86_64
+installed = libexttextcat-3.4.8-1-x86_64
+installed = libfdk-aac-2.0.3-2-x86_64
+installed = libffi-3.5.2-1-x86_64
+installed = libfido2-1.17.0-1-x86_64
+installed = libfontenc-1.1.9-1-x86_64
+installed = libfreeaptx-0.2.2-1-x86_64
+installed = libfreehand-0.1.3-1-x86_64
+installed = libfyaml-0.9.6-2-x86_64
+installed = libgcc-16.1.1+r12+g301eb08fa2c5-1-x86_64
+installed = libgcrypt-1.12.2-1-x86_64
+installed = libgfortran-16.1.1+r12+g301eb08fa2c5-1-x86_64
+installed = libgirepository-1.86.0-2-x86_64
+installed = libgit2-1:1.9.4-1-x86_64
+installed = libglvnd-1.7.0-3-x86_64
+installed = libgomp-16.1.1+r12+g301eb08fa2c5-1-x86_64
+installed = libgpg-error-1.61-1-x86_64
+installed = libgtop-2.41.3-2-x86_64
+installed = libgudev-238-3-x86_64
+installed = libice-1.1.2-1-x86_64
+installed = libidn2-2.3.8-1-x86_64
+installed = libiec61883-1.2.0-9-x86_64
+installed = libimobiledevice-1.4.0-2-x86_64
+installed = libimobiledevice-glue-1.3.2-1-x86_64
+installed = libinih-62-2-x86_64
+installed = libinput-1.31.3-1-x86_64
+installed = libisl-0.27-1-x86_64
+installed = libisoburn-1.5.8.2-1-x86_64
+installed = libisofs-1.5.8.2-1-x86_64
+installed = libixion-0.20.0-7-x86_64
+installed = libjpeg-turbo-3.1.4.1-1-x86_64
+installed = libjxl-0.11.2-2-x86_64
+installed = libksba-1.8.0-1-x86_64
+installed = liblangtag-0.6.8-1-x86_64
+installed = liblc3-1.1.3-2-x86_64
+installed = libldac-2.0.2.3-3-x86_64
+installed = libldap-2.6.13-1-x86_64
+installed = liblqr-0.4.3-1-x86_64
+installed = liblsan-16.1.1+r12+g301eb08fa2c5-1-x86_64
+installed = libmakepkg-dropins-20-1-any
+installed = libmalcontent-0.14.0-4-x86_64
+installed = libmd-1.2.0-1-x86_64
+installed = libmm-glib-1.24.2-1-x86_64
+installed = libmnl-1.0.5-2-x86_64
+installed = libmodplug-0.8.9.0-7-x86_64
+installed = libmpc-1.4.1-1-x86_64
+installed = libmpdclient-2.23-1-x86_64
+installed = libmspub-0.1.5-1-x86_64
+installed = libmwaw-0.3.22-4-x86_64
+installed = libmysofa-1.3.4-1-x86_64
+installed = libndp-1.9-1-x86_64
+installed = libnet-2:1.3-2-x86_64
+installed = libnetfilter_conntrack-1.1.1-1-x86_64
+installed = libnewt-0.52.25-2-x86_64
+installed = libnfnetlink-1.0.2-2-x86_64
+installed = libnftnl-1.3.1-1-x86_64
+installed = libnghttp2-1.69.0-1-x86_64
+installed = libnghttp3-1.16.0-1-x86_64
+installed = libngtcp2-1.23.0-1-x86_64
+installed = libnice-0.1.23-1-x86_64
+installed = libnl-3.12.0-1-x86_64
+installed = libnm-1.56.1-1-x86_64
+installed = libnma-1.10.6-3-x86_64
+installed = libnma-common-1.10.6-3-x86_64
+installed = libnotify-0.8.8-1-x86_64
+installed = libnsl-2.0.1-2-x86_64
+installed = libnumbertext-1.0.11-3-x86_64
+installed = libobjc-16.1.1+r12+g301eb08fa2c5-1-x86_64
+installed = libodfgen-0.1.8-5-x86_64
+installed = libogg-1.3.6-1-x86_64
+installed = libopenmpt-0.8.7-1-x86_64
+installed = libopenrazer-0.4.0-1-x86_64
+installed = liborcus-0.21.0-6-x86_64
+installed = libp11-kit-0.26.2-1-x86_64
+installed = libpagemaker-0.0.4-5-x86_64
+installed = libpcap-1.10.6-1-x86_64
+installed = libpciaccess-0.19-1-x86_64
+installed = libpgm-5.3.128-4-x86_64
+installed = libpipewire-1:1.6.6-1-x86_64
+installed = libplacebo-7.360.1-2-x86_64
+installed = libplist-2.7.0-3-x86_64
+installed = libpng-1.6.58-1-x86_64
+installed = libproxy-0.5.12-1-x86_64
+installed = libpsl-0.21.5-2-x86_64
+installed = libpulse-17.0+r98+gb096704c0-1-x86_64
+installed = libpwquality-1.4.5-7-x86_64
+installed = libquadmath-16.1.1+r12+g301eb08fa2c5-1-x86_64
+installed = libqxp-0.0.3-1-x86_64
+installed = libraqm-0.10.5-1-x86_64
+installed = libraw1394-2.1.2-4-x86_64
+installed = libreoffice-fresh-26.2.4-1-x86_64
+installed = librevenge-0.0.5-4-x86_64
+installed = librsvg-2:2.62.3-1-x86_64
+installed = librsync-1:2.3.4-2-x86_64
+installed = libsamplerate-0.2.2-3-x86_64
+installed = libsasl-2.1.28-5-x86_64
+installed = libseccomp-2.6.0-1-x86_64
+installed = libsecret-0.21.7-1-x86_64
+installed = libsigc++-2.12.2-1-x86_64
+installed = libsigc++-3.0-3.8.1-1-x86_64
+installed = libsixel-1.10.5-1-x86_64
+installed = libsm-1.2.6-1-x86_64
+installed = libsndfile-1.2.2-4-x86_64
+installed = libsodium-1.0.22-1-x86_64
+installed = libsoup3-3.6.6-2-x86_64
+installed = libsoxr-0.1.3-4-x86_64
+installed = libssh-0.12.0-1-x86_64
+installed = libssh2-1.11.1-1-x86_64
+installed = libstaroffice-0.0.7-5-x86_64
+installed = libstdc++-16.1.1+r12+g301eb08fa2c5-1-x86_64
+installed = libstemmer-3.1.1-1-x86_64
+installed = libsysprof-capture-50.0-2-x86_64
+installed = libtasn1-4.21.0-1-x86_64
+installed = libtatsu-1.0.5-1-x86_64
+installed = libteam-1.32-3-x86_64
+installed = libthai-0.1.30-1-x86_64
+installed = libtheora-1.2.0-1-x86_64
+installed = libtiff-4.7.1-2-x86_64
+installed = libtirpc-1.3.7-1-x86_64
+installed = libtommath-1.3.0-2-x86_64
+installed = libtool-2.6.1-1-x86_64
+installed = libtsan-16.1.1+r12+g301eb08fa2c5-1-x86_64
+installed = libubsan-16.1.1+r12+g301eb08fa2c5-1-x86_64
+installed = libunibreak-7.0-1-x86_64
+installed = libunistring-1.4.2-1-x86_64
+installed = libunwind-1.8.2-1-x86_64
+installed = liburing-2.14-1-x86_64
+installed = libusb-1.0.30-1-x86_64
+installed = libusbmuxd-2.1.1-2-x86_64
+installed = libutf8proc-2.11.3-1-x86_64
+installed = libuv-1.52.1-1-x86_64
+installed = libva-2.23.0-1-x86_64
+installed = libva-nvidia-driver-0.0.17-1-x86_64
+installed = libvdpau-1.5-4-x86_64
+installed = libverto-0.3.2-6-x86_64
+installed = libvisio-0.1.11-1-x86_64
+installed = libvorbis-1.3.7-4-x86_64
+installed = libvpl-2.16.0-2-x86_64
+installed = libvpx-1.16.0-3-x86_64
+installed = libwacom-2.19.0-1-x86_64
+installed = libwebp-1.6.0-2-x86_64
+installed = libwireplumber-0.5.14-1-x86_64
+installed = libwpd-0.10.3-6-x86_64
+installed = libwps-0.4.14-4-x86_64
+installed = libx11-1.8.13-1-x86_64
+installed = libxau-1.0.12-1-x86_64
+installed = libxcb-1.17.0-1-x86_64
+installed = libxcomposite-0.4.7-1-x86_64
+installed = libxcrypt-4.5.2-1-x86_64
+installed = libxcrypt-compat-4.5.2-1-x86_64
+installed = libxcursor-1.2.3-1-x86_64
+installed = libxcvt-0.1.3-1-x86_64
+installed = libxdamage-1.1.7-1-x86_64
+installed = libxdmcp-1.1.5-2-x86_64
+installed = libxext-1.3.7-1-x86_64
+installed = libxfce4ui-4.20.2-1-x86_64
+installed = libxfce4util-4.20.1-1-x86_64
+installed = libxfixes-6.0.2-1-x86_64
+installed = libxfont2-2.0.7-1-x86_64
+installed = libxft-2.3.9-1-x86_64
+installed = libxi-1.8.3-1-x86_64
+installed = libxinerama-1.1.6-1-x86_64
+installed = libxkbcommon-1.13.2-1-x86_64
+installed = libxkbcommon-x11-1.13.2-1-x86_64
+installed = libxkbfile-1.2.0-1-x86_64
+installed = libxml2-2.15.3-1-x86_64
+installed = libxml2-legacy-2.13.9-2-x86_64
+installed = libxmlb-0.3.27-1-x86_64
+installed = libxmp-4.7.0-1-x86_64
+installed = libxmu-1.3.1-1-x86_64
+installed = libxpresent-1.0.2-1-x86_64
+installed = libxrandr-1.5.5-1-x86_64
+installed = libxrender-0.9.12-1-x86_64
+installed = libxshmfence-1.3.3-1-x86_64
+installed = libxslt-1.1.45-2-x86_64
+installed = libxss-1.2.5-1-x86_64
+installed = libxt-1.3.1-1-x86_64
+installed = libxtst-1.2.5-1-x86_64
+installed = libxv-1.0.13-1-x86_64
+installed = libxxf86vm-1.1.7-1-x86_64
+installed = libyaml-0.2.5-3-x86_64
+installed = libyuv-r2426+464c51a03-1-x86_64
+installed = libzip-1.11.4-1-x86_64
+installed = libzmf-0.0.2-20-x86_64
+installed = licenses-20240728-1-any
+installed = lilv-0.26.4-1-x86_64
+installed = linux-7.0.11.arch1-1-x86_64
+installed = linux-api-headers-7.0-1-x86_64
+installed = linux-firmware-20260519-1-any
+installed = linux-firmware-amdgpu-20260519-1-any
+installed = linux-firmware-atheros-20260519-1-any
+installed = linux-firmware-broadcom-20260519-1-any
+installed = linux-firmware-cirrus-20260519-1-any
+installed = linux-firmware-intel-20260519-1-any
+installed = linux-firmware-mediatek-20260519-1-any
+installed = linux-firmware-nvidia-20260519-1-any
+installed = linux-firmware-other-20260519-1-any
+installed = linux-firmware-radeon-20260519-1-any
+installed = linux-firmware-realtek-20260519-1-any
+installed = linux-firmware-whence-20260519-1-any
+installed = linux-headers-7.0.11.arch1-1-x86_64
+installed = lld-22.1.6-1-x86_64
+installed = llhttp-9.3.1-1-x86_64
+installed = llvm-libs-22.1.6-1-x86_64
+installed = lm_sensors-1:3.6.2-1-x86_64
+installed = lmdb-0.9.35-1-x86_64
+installed = lpsolve-5.5.2.14-1-x86_64
+installed = lsb-release-2.0.r55.a25a4fc-1-any
+installed = lsof-4.99.6-1-x86_64
+installed = lua-5.5.0-2-x86_64
+installed = lua54-5.4.8-6-x86_64
+installed = luajit-2.1.1780076327+b925b3e-1-x86_64
+installed = lug-helper-1:4.13-1-any
+installed = lv2-1.18.10-2-x86_64
+installed = lz4-1:1.10.0-2-x86_64
+installed = lzo-2.10-5-x86_64
+installed = m4-1.4.21-2-x86_64
+installed = mailcap-2.1.54-2-any
+installed = make-4.4.1-3-x86_64
+installed = mako-1.11.0-1-x86_64
+installed = md4c-0.5.3-1-x86_64
+installed = mesa-1:26.1.2-1-x86_64
+installed = meson-1.11.1-3-any
+installed = mingw-w64-binutils-2.46.0-1-x86_64
+installed = mingw-w64-crt-14.0.0-1-any
+installed = mingw-w64-gcc-16.1.0-1-x86_64
+installed = mingw-w64-headers-14.0.0-1-any
+installed = mingw-w64-winpthreads-14.0.0-1-any
+installed = minizip-1:1.3.2-3-x86_64
+installed = mkinitcpio-41-4-any
+installed = mkinitcpio-busybox-1.36.1-1-x86_64
+installed = mobile-broadband-provider-info-20251101-1-any
+installed = mpdecimal-4.0.1-3-x86_64
+installed = mpfr-4.2.2-1-x86_64
+installed = mpg123-1.33.5-1-x86_64
+installed = mpv-1:0.41.0-3-x86_64
+installed = mpvpaper-1.8-1-x86_64
+installed = mtdev-1.1.7-1-x86_64
+installed = mtools-1:4.0.49-1-x86_64
+installed = mujs-1.3.9-1-x86_64
+installed = muparser-2.3.5-2-x86_64
+installed = nano-9.0-1-x86_64
+installed = ncurses-6.6-2-x86_64
+installed = neon-0.37.1-1-x86_64
+installed = netavark-1.17.2-2-x86_64
+installed = nettle-4.0-1-x86_64
+installed = network-manager-applet-1.36.0-2-x86_64
+installed = networkmanager-1.56.1-1-x86_64
+installed = nftables-1:1.1.6-3-x86_64
+installed = ninja-1.13.2-3-x86_64
+installed = nm-connection-editor-1.36.0-2-x86_64
+installed = noto-fonts-1:2026.06.01-1-any
+installed = noto-fonts-cjk-20240730-1-any
+installed = noto-fonts-emoji-1:2.051-1-any
+installed = noto-fonts-extra-1:2026.06.01-1-any
+installed = npth-1.8-1-x86_64
+installed = nspr-4.39-1-x86_64
+installed = nss-3.124-1-x86_64
+installed = nvidia-open-610.43.02-2-x86_64
+installed = nvidia-utils-610.43.02-2-x86_64
+installed = ocl-icd-2.3.4-1-x86_64
+installed = onetbb-2023.0.0-1-x86_64
+installed = oniguruma-6.9.10-1-x86_64
+installed = openal-1.25.2-1-x86_64
+installed = opencore-amr-0.1.6-2-x86_64
+installed = openjpeg2-2.5.4-1-x86_64
+installed = openrazer-daemon-3.12.2-2-any
+installed = openrazer-driver-dkms-3.12.2-2-any
+installed = openssh-10.3p1-1-x86_64
+installed = openssl-3.6.3-1-x86_64
+installed = opus-1.6.1-1-x86_64
+installed = opusfile-0.12-4-x86_64
+installed = orc-0.4.42-1-x86_64
+installed = os-prober-1.84-1-x86_64
+installed = ostree-2025.7-3-x86_64
+installed = p11-kit-0.26.2-1-x86_64
+installed = pacman-7.1.0.r9.g54d9411-2-x86_64
+installed = pacman-mirrorlist-20260610-1-any
+installed = pahole-1:1.31-2-x86_64
+installed = pam-1.7.2-2-x86_64
+installed = pambase-20250719-1-any
+installed = pango-1:1.57.1-1-x86_64
+installed = pangomm-2.46.4-3-x86_64
+installed = pangomm-2.48-2.56.1-2-x86_64
+installed = papirus-icon-theme-20250501-1-any
+installed = parted-3.7-1-x86_64
+installed = paru-2.1.0-2-x86_64
+installed = paru-debug-2.1.0-2-x86_64
+installed = passt-2026_05_26.038c51e-1-x86_64
+installed = patch-2.8-1-x86_64
+installed = pavucontrol-1:6.2-1-x86_64
+installed = pciutils-3.15.0-1-x86_64
+installed = pcre-8.45-4-x86_64
+installed = pcre2-10.47-1-x86_64
+installed = pcsclite-2.5.1-1-x86_64
+installed = perl-5.42.2-1-x86_64
+installed = perl-error-0.17030-3-any
+installed = perl-mailtools-2.22-3-any
+installed = perl-timedate-2.35-1-any
+installed = pinentry-1.3.2-2-x86_64
+installed = pipewire-1:1.6.6-1-x86_64
+installed = pipewire-audio-1:1.6.6-1-x86_64
+installed = pipewire-pulse-1:1.6.6-1-x86_64
+installed = pixman-0.46.4-1-x86_64
+installed = pkgconf-2.5.1-1-x86_64
+installed = playerctl-2.4.1-5-x86_64
+installed = podman-5.8.3-1-x86_64
+installed = podman-compose-1.6.0-1-any
+installed = podman-desktop-1.27.2-2-x86_64
+installed = polkit-127-3-x86_64
+installed = polkit-qt6-0.201.1-1-x86_64
+installed = poppler-26.05.0-1-x86_64
+installed = popt-1.19-2-x86_64
+installed = portaudio-1:19.7.0-4-x86_64
+installed = portmidi-1:2.0.8-1-x86_64
+installed = pps-tools-1.0.3-2-x86_64
+installed = procps-ng-4.0.6-1-x86_64
+installed = protobuf-35.0-1-x86_64
+installed = protobuf-c-1.5.2-10-x86_64
+installed = psmisc-23.7-2-x86_64
+installed = pugixml-1.15-3-x86_64
+installed = python-3.14.5-1-x86_64
+installed = python-attrs-26.1.0-1-any
+installed = python-autocommand-2.2.2-9-any
+installed = python-cffi-2.0.0-2-x86_64
+installed = python-click-8.3.3-1-any
+installed = python-cryptography-48.0.1-1-x86_64
+installed = python-daemonize-2.5.0-10-any
+installed = python-dbus-1.4.0-2-x86_64
+installed = python-dotenv-1.2.2-1-any
+installed = python-filelock-3.29.0-1-any
+installed = python-gobject-3.56.3-1-x86_64
+installed = python-jaraco.collections-5.1.0-3-any
+installed = python-jaraco.context-6.1.2-1-any
+installed = python-jaraco.functools-4.1.0-3-any
+installed = python-jaraco.text-4.0.0-4-any
+installed = python-mako-1.3.11-1-any
+installed = python-markdown-3.10.2-1-any
+installed = python-markupsafe-3.0.3-1-x86_64
+installed = python-more-itertools-11.1.0-1-any
+installed = python-numpy-2.4.6-1-x86_64
+installed = python-openrazer-3.12.2-2-any
+installed = python-packaging-26.2-1-any
+installed = python-pip-26.1.2-1-any
+installed = python-pkg_resources-81.0.0-1-any
+installed = python-platformdirs-4.10.0-1-any
+installed = python-protobuf-35.0-1-x86_64
+installed = python-pycparser-3.00-1-any
+installed = python-pygame-2.6.1-5-x86_64
+installed = python-pyudev-0.24.4-1-any
+installed = python-setproctitle-1.3.7-2-x86_64
+installed = python-setuptools-1:82.0.1-1-any
+installed = python-tqdm-4.68.1-1-any
+installed = python-typing_extensions-4.15.0-3-any
+installed = python-wheel-0.47.0-1-any
+installed = python-yaml-6.0.3-2-x86_64
+installed = qt5-base-5.15.19+kde+r96-1-x86_64
+installed = qt5-declarative-5.15.19+kde+r23-1-x86_64
+installed = qt5-graphicaleffects-5.15.19-1-x86_64
+installed = qt5-quickcontrols2-5.15.19+kde+r5-1-x86_64
+installed = qt5-svg-5.15.19+kde+r5-1-x86_64
+installed = qt5-translations-5.15.19-1-any
+installed = qt5-wayland-5.15.19+kde+r55-1-x86_64
+installed = qt6-5compat-6.11.1-1-x86_64
+installed = qt6-base-6.11.1-1-x86_64
+installed = qt6-declarative-6.11.1-3-x86_64
+installed = qt6-shadertools-6.11.1-1-x86_64
+installed = qt6-svg-6.11.1-1-x86_64
+installed = qt6-tools-6.11.1-1-x86_64
+installed = qt6-translations-6.11.1-1-any
+installed = qt6-wayland-6.11.1-1-x86_64
+installed = raptor-2.0.16-9-x86_64
+installed = rasqal-1:0.9.33-9-x86_64
+installed = rav1e-0.8.1-2-x86_64
+installed = razergenie-1.3.0-1-x86_64
+installed = re2-2:2025.11.05-4-x86_64
+installed = readline-8.3.003-1-x86_64
+installed = redland-1:1.0.17-10-x86_64
+installed = reflector-2023-5-any
+installed = resvg-0.47.0-2-x86_64
+installed = rhash-1.4.6-1-x86_64
+installed = rsync-3.4.4-1-x86_64
+installed = rtkit-0.14-1-x86_64
+installed = rubberband-4.0.0-2-x86_64
+installed = rustup-1.29.0-2-x86_64
+installed = sbc-2.2-1-x86_64
+installed = scdoc-1.11.4-1-x86_64
+installed = sdbus-cpp-2.3.1-1-x86_64
+installed = sddm-0.21.0-7-x86_64
+installed = sddm-catppuccin-git-r42.10831de-1-any
+installed = sdl2-compat-2.32.70-1-x86_64
+installed = sdl2_image-2.8.12-1-x86_64
+installed = sdl2_mixer-2.8.2-1-x86_64
+installed = sdl2_ttf-2.24.0-2-x86_64
+installed = sdl3-3.4.10-1-x86_64
+installed = sdl3_ttf-3.2.2-3-x86_64
+installed = seatd-0.9.3-1-x86_64
+installed = sed-4.10-1-x86_64
+installed = serd-0.32.10-1-x86_64
+installed = shaderc-2026.2-1-x86_64
+installed = shadow-4.18.0-1-x86_64
+installed = shared-mime-info-2.4-3-x86_64
+installed = slang-2.3.3-4-x86_64
+installed = slurp-1.5.0-2-x86_64
+installed = smartmontools-7.5-1-x86_64
+installed = snappy-1.2.2-3-x86_64
+installed = sndio-1.10.0-1-x86_64
+installed = sord-0.16.22-1-x86_64
+installed = sound-theme-freedesktop-0.8-6-any
+installed = source-highlight-3.1.9-18-x86_64
+installed = spandsp-0.0.6-7-x86_64
+installed = spdlog-1.17.0-2-x86_64
+installed = speex-1.2.1-2-x86_64
+installed = speexdsp-1.2.1-2-x86_64
+installed = spirv-tools-1:1.4.350.0-1-x86_64
+installed = sqlite-3.53.2-1-x86_64
+installed = squashfs-tools-4.7.5-1-x86_64
+installed = sratom-0.6.22-1-x86_64
+installed = srt-1.5.5-1-x86_64
+installed = startup-notification-0.12-9-x86_64
+installed = steam-1.0.0.85-7-x86_64
+installed = steam-devices-1.0.0.85-7-x86_64
+installed = sudo-1.9.17.p2-2-x86_64
+installed = svt-av1-4.1.0-1-x86_64
+installed = systemd-260.2-2-x86_64
+installed = systemd-libs-260.2-2-x86_64
+installed = systemd-sysvcompat-260.2-2-x86_64
+installed = tar-1.35-2-x86_64
+installed = tcl-8.6.16-1-x86_64
+installed = tdb-1.4.15-1-x86_64
+installed = texinfo-7.3-1-x86_64
+installed = thunar-4.20.8-3-x86_64
+installed = tinysparql-3.11.1-1-x86_64
+installed = tk-8.6.16-1-x86_64
+installed = tomlplusplus-3.4.0-2-x86_64
+installed = tpm2-tss-4.1.3-1-x86_64
+installed = tslib-1.24-1-x86_64
+installed = ttf-jetbrains-mono-nerd-3.4.0-2-any
+installed = tzdata-2026b-1-x86_64
+installed = uchardet-0.0.8-4-x86_64
+installed = uefitool-debug-1:0.28.0-3-x86_64
+installed = unzip-6.0-23-x86_64
+installed = upower-1.91.2-1-x86_64
+installed = usbutils-019-1-x86_64
+installed = util-linux-2.42.1-1-x86_64
+installed = util-linux-libs-2.42.1-1-x86_64
+installed = v4l-utils-1.32.0-2-x86_64
+installed = vapoursynth-76-1-x86_64
+installed = vid.stab-1.1.1-2-x86_64
+installed = vim-9.2.0623-1-x86_64
+installed = vim-runtime-9.2.0623-1-x86_64
+installed = vmaf-3.1.0-1-x86_64
+installed = vmware-keymaps-1.0-3-any
+installed = vmware-workstation-26H1-3-x86_64
+installed = vscodium-bin-1.121.03429-1-x86_64
+installed = vulkan-icd-loader-1.4.350.0-1-x86_64
+installed = vulkan-tools-1.4.350.0-1-x86_64
+installed = waybar-0.15.0-2-x86_64
+installed = wayland-1.25.0-1-x86_64
+installed = wayland-protocols-1.49-1-any
+installed = webrtc-audio-processing-1-1.3-5-x86_64
+installed = which-2.25-1-x86_64
+installed = winboat-bin-0.9.0-1-x86_64
+installed = wine-staging-11.10-1-x86_64
+installed = wireplumber-0.5.14-1-x86_64
+installed = wl-clipboard-1:2.3.0-1-x86_64
+installed = wlogout-1.2.2-0-x86_64
+installed = woff2-font-awesome-7.2.0-1-any
+installed = wpa_supplicant-2:2.11-5-x86_64
+installed = x264-3:0.165.r3222.b35605a-2-x86_64
+installed = x265-4.1-1-x86_64
+installed = xautomation-1.09-6-x86_64
+installed = xcb-proto-1.17.0-4-any
+installed = xcb-util-0.4.1-2-x86_64
+installed = xcb-util-cursor-0.1.6-1-x86_64
+installed = xcb-util-errors-1.0.1-2-x86_64
+installed = xcb-util-image-0.4.1-3-x86_64
+installed = xcb-util-keysyms-0.4.1-5-x86_64
+installed = xcb-util-renderutil-0.3.10-2-x86_64
+installed = xcb-util-wm-0.4.2-2-x86_64
+installed = xdg-dbus-proxy-0.1.7-1-x86_64
+installed = xdg-desktop-portal-1.22.0-1-x86_64
+installed = xdg-desktop-portal-gtk-1.15.3-1-x86_64
+installed = xdg-desktop-portal-hyprland-1.3.12-2-x86_64
+installed = xdg-user-dirs-0.20-1-x86_64
+installed = xdg-utils-1.2.1-2-any
+installed = xf86-input-libinput-1.5.0-1-x86_64
+installed = xfconf-4.20.0-2-x86_64
+installed = xkeyboard-config-2.47-1-any
+installed = xmlsec-1.3.11-1-x86_64
+installed = xorg-fonts-encodings-1.1.0-2-any
+installed = xorg-server-21.1.23-1-x86_64
+installed = xorg-server-common-21.1.23-1-x86_64
+installed = xorg-setxkbmap-1.3.5-1-x86_64
+installed = xorg-xauth-1.1.5-1-x86_64
+installed = xorg-xkbcomp-1.5.0-1-x86_64
+installed = xorg-xprop-1.2.8-1-x86_64
+installed = xorg-xrandr-1.5.4-1-x86_64
+installed = xorg-xwayland-24.1.12-1-x86_64
+installed = xorgproto-2025.1-1-any
+installed = xvidcore-1.3.7-3-x86_64
+installed = xxhash-0.8.3-1-x86_64
+installed = xz-5.8.3-1-x86_64
+installed = yaml-cpp-0.9.0-1-x86_64
+installed = yay-12.6.0-1-x86_64
+installed = yay-debug-12.6.0-1-x86_64
+installed = zenity-4.2.2-1-x86_64
+installed = zeromq-4.3.5-3-x86_64
+installed = zimg-3.0.6-1-x86_64
+installed = zint-2.16.0-2-x86_64
+installed = zix-0.8.0-1-x86_64
+installed = zlib-1:1.3.2-3-x86_64
+installed = zlib-ng-2.3.3-1-x86_64
+installed = zstd-1.5.7-3-x86_64
+installed = zxing-cpp-3.0.2-1-x86_64
diff --git a/pkgbuilds/python-meh/pkg/python-meh/.MTREE b/pkgbuilds/python-meh/pkg/python-meh/.MTREE
new file mode 100644
index 0000000..6dcfa5f
Binary files /dev/null and b/pkgbuilds/python-meh/pkg/python-meh/.MTREE differ
diff --git a/pkgbuilds/python-meh/pkg/python-meh/.PKGINFO b/pkgbuilds/python-meh/pkg/python-meh/.PKGINFO
new file mode 100644
index 0000000..afc8897
--- /dev/null
+++ b/pkgbuilds/python-meh/pkg/python-meh/.PKGINFO
@@ -0,0 +1,16 @@
+# Generated by makepkg 7.1.0
+# using fakeroot version 1.37.2
+pkgname = python-meh
+pkgbase = python-meh
+xdata = pkgtype=pkg
+pkgver = 0.43-1
+pkgdesc = Python exception handling library (dépendance Anaconda)
+url = https://github.com/rhinstaller/python-meh
+builddate = 1781307281
+packager = Unknown Packager
+size = 170289
+arch = any
+license = GPL-2.0-or-later
+depend = python
+depend = python-gobject
+depend = python-dbus
diff --git a/pkgbuilds/python-meh/pkg/python-meh/usr/lib/python3.14/site-packages/meh/__init__.py b/pkgbuilds/python-meh/pkg/python-meh/usr/lib/python3.14/site-packages/meh/__init__.py
new file mode 100644
index 0000000..6d554af
--- /dev/null
+++ b/pkgbuilds/python-meh/pkg/python-meh/usr/lib/python3.14/site-packages/meh/__init__.py
@@ -0,0 +1,139 @@
+#
+# __init__.py - Basic stuff for exception handling
+#
+# Copyright (C) 2009 Red Hat, Inc.
+# All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#
+# Author(s): Chris Lumens
+#
+
+from collections import namedtuple
+
+# These constants represent the return values of buttons on the initial
+# exception handling dialog - the dialog that first pops up when an
+# exception is hit.
+MAIN_RESPONSE_DEBUG = 0
+MAIN_RESPONSE_SAVE = 1
+MAIN_RESPONSE_QUIT = 2
+MAIN_RESPONSE_SHELL = 3
+MAIN_RESPONSE_NONE = 4
+
+class ConfigError(Exception):
+ """Exception class for the configuration related errors."""
+
+ pass
+
+class Config(object):
+ """Hold configuration info useful throughout the exception handling
+ classes. This prevents having to pass a bunch of arguments to a
+ bunch of different functions. A Config instance must be created
+ before creating an ExceptionHandler instance.
+ """
+ def __init__(self, *args, **kwargs):
+ """Create a new Config instance. Arguments may be passed in to
+ set any of the configuration values. Unknown arguments will
+ be ignored. Instance attributes:
+
+ attrSkipList -- A list of strings. When handling a traceback,
+ any attributes found with the same name as an
+ element of this list will not be written to
+ the dump. This is to prevent writing potentially
+ sensitive information like passwords. The names
+ must be given without the leading name of the
+ object passed to handler.install(). For instance,
+ if handler.install() gets an Anaconda instance
+ with the name "anaconda" and you want to skip
+ anaconda.id.rootPassword, "id.rootPassword" should
+ be listed in attrSkipList.
+ fileList -- A list of files to find on the system and add
+ to the traceback dump.
+ localSkipList -- A list of strings. When handling a traceback,
+ any local variables found with their name containing
+ an element of this list will not be written to
+ the dump. This is subtely different from
+ attrSkipList.
+ callbackDict -- A dictionary having item names as keys and pairs
+ (function, attchmnt_only) as values.
+ @see register_callback
+ programName -- The name of the erroring program.
+ programVersion -- The version number of the erroring program.
+ Both programName and programVersion are used
+ throughout the exception handler, so must be
+ set.
+ programArch -- The architecture of the erroring program.
+ """
+ self.attrSkipList = []
+ self.fileList = []
+ self.localSkipList = []
+ self.callbackDict = dict()
+ self.programName = None
+ self.programVersion = None
+ self.programArch = None
+
+ # Override the defaults set above with whatever's passed in as an
+ # argument. Unknown arguments get thrown away.
+ for (key, val) in kwargs.items():
+ if key in self.__dict__:
+ self.__dict__[key] = val
+
+ # Make sure required things are set.
+ if not self.programName:
+ raise ValueError("programName must be set.")
+
+ if not self.programVersion:
+ raise ValueError("programVersion must be set.")
+
+ def register_callback(self, item_name, callback, attchmnt_only=False,
+ override=False):
+ """
+ Register new callback that will be called when data about the
+ crash is collected. The returned value will be included as the
+ 'item_name' item.
+
+ :param item_name: name of the item storing the value returned by the
+ 'callback' function
+ :type item_name: string
+ :param callback: a function to be called
+ :type callback: a function of type: () -> string
+ :param attchmnt_only: whether the returned valued should be included only
+ as an attachment or also in the main '*-tb' file
+ (defaults to False)
+ :type attchmnt_only: bool
+ :param override: whether to override the previously registered callback
+ with the same item name or not (may raise exception)
+ :type override: bool
+
+ :raise ConfigError: if callback with the 'item_name' has already been
+ registered and 'override' is not set to True
+ :return: None
+
+ """
+
+ if item_name not in self.callbackDict or override:
+ self.callbackDict[item_name] = (callback, attchmnt_only)
+ else:
+ msg = "Callback with name '%s' already registered" % item_name
+ raise ConfigError(msg)
+
+# type, value and stack are items provided by Python for the exception handler
+ExceptionInfo = namedtuple("ExceptionInfo", ["type", "value", "stack"])
+
+# ExceptionInfo instance plus the object that should be dumped
+DumpInfo = namedtuple("DumpInfo", ["exc_info", "object"])
+
+# information about a package (as provided e.g. by rpm)
+PackageInfo = namedtuple("PackageInfo", ["name", "version", "release", "epoch",
+ "arch"])
diff --git a/pkgbuilds/python-meh/pkg/python-meh/usr/lib/python3.14/site-packages/meh/__pycache__/__init__.cpython-314.pyc b/pkgbuilds/python-meh/pkg/python-meh/usr/lib/python3.14/site-packages/meh/__pycache__/__init__.cpython-314.pyc
new file mode 100644
index 0000000..d9da02f
Binary files /dev/null and b/pkgbuilds/python-meh/pkg/python-meh/usr/lib/python3.14/site-packages/meh/__pycache__/__init__.cpython-314.pyc differ
diff --git a/pkgbuilds/python-meh/pkg/python-meh/usr/lib/python3.14/site-packages/meh/__pycache__/dump.cpython-314.pyc b/pkgbuilds/python-meh/pkg/python-meh/usr/lib/python3.14/site-packages/meh/__pycache__/dump.cpython-314.pyc
new file mode 100644
index 0000000..0cbac5c
Binary files /dev/null and b/pkgbuilds/python-meh/pkg/python-meh/usr/lib/python3.14/site-packages/meh/__pycache__/dump.cpython-314.pyc differ
diff --git a/pkgbuilds/python-meh/pkg/python-meh/usr/lib/python3.14/site-packages/meh/__pycache__/handler.cpython-314.pyc b/pkgbuilds/python-meh/pkg/python-meh/usr/lib/python3.14/site-packages/meh/__pycache__/handler.cpython-314.pyc
new file mode 100644
index 0000000..f75c7de
Binary files /dev/null and b/pkgbuilds/python-meh/pkg/python-meh/usr/lib/python3.14/site-packages/meh/__pycache__/handler.cpython-314.pyc differ
diff --git a/pkgbuilds/python-meh/pkg/python-meh/usr/lib/python3.14/site-packages/meh/__pycache__/network.cpython-314.pyc b/pkgbuilds/python-meh/pkg/python-meh/usr/lib/python3.14/site-packages/meh/__pycache__/network.cpython-314.pyc
new file mode 100644
index 0000000..acc93af
Binary files /dev/null and b/pkgbuilds/python-meh/pkg/python-meh/usr/lib/python3.14/site-packages/meh/__pycache__/network.cpython-314.pyc differ
diff --git a/pkgbuilds/python-meh/pkg/python-meh/usr/lib/python3.14/site-packages/meh/__pycache__/safe_string.cpython-314.pyc b/pkgbuilds/python-meh/pkg/python-meh/usr/lib/python3.14/site-packages/meh/__pycache__/safe_string.cpython-314.pyc
new file mode 100644
index 0000000..f74411f
Binary files /dev/null and b/pkgbuilds/python-meh/pkg/python-meh/usr/lib/python3.14/site-packages/meh/__pycache__/safe_string.cpython-314.pyc differ
diff --git a/pkgbuilds/python-meh/pkg/python-meh/usr/lib/python3.14/site-packages/meh/dump.py b/pkgbuilds/python-meh/pkg/python-meh/usr/lib/python3.14/site-packages/meh/dump.py
new file mode 100644
index 0000000..c746e57
--- /dev/null
+++ b/pkgbuilds/python-meh/pkg/python-meh/usr/lib/python3.14/site-packages/meh/dump.py
@@ -0,0 +1,551 @@
+#
+# dump.py - general exception formatting and saving
+#
+# Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2009 Red Hat, Inc.
+# All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#
+# Author(s): Matt Wilson
+# Erik Troan
+# Chris Lumens
+#
+# pylint: disable=bare-except,broad-except
+
+import copy
+import inspect
+import os
+import traceback
+import sys
+import codecs
+import six
+from meh import PackageInfo
+from meh.safe_string import SafeStr
+
+# Python 2/3 compatibilty
+try:
+ longtype = long # pylint: disable=undefined-variable
+except NameError:
+ longtype = int
+
+try:
+ unitype = unicode # pylint: disable=undefined-variable
+except NameError:
+ unitype = str
+
+class ExceptionDump(object):
+ """This class represents a traceback and contains several useful methods
+ for manipulating a traceback. In general, clients should not have to
+ use this class. It is mainly for internal use.
+ """
+ def __init__(self, exc_info, config_obj):
+ """Create a new ExceptionDump instance. Instance attributes:
+
+ :param exc_info: info about the exception provided by Python
+ :type exc_info: an instance of the meh.ExceptionInfo class
+ :param config_obj: configuration for python-meh
+ :type config_obj: an instance of the meh.Config class
+ """
+
+ if inspect.istraceback(exc_info.stack):
+ self.stack = inspect.getinnerframes(exc_info.stack)
+ else:
+ self.stack = exc_info.stack
+
+ self.conf = config_obj
+ self.type = exc_info.type
+ self.value = exc_info.value
+
+ self._dumpHash = {}
+
+ @property
+ def desc(self):
+ # The description is a single line of text that should be used anywhere
+ # the bug needs to quickly be summarized. The most obvious example of
+ # this is when saving to bugzilla. We can populate the UI with this
+ # string so the user doesn't need to come up with one.
+ if self.type and self.value:
+ return traceback.format_exception_only(self.type, self.value)[0].strip()
+ else:
+ return ""
+
+ def _get_environment_info(self):
+ """
+ Returns dictionary containing these items for a bugreport header
+
+ architecture
+ cmdline
+ component
+ executable
+ kernel
+ package
+ release
+ other involved packages
+
+ :rtype: dict (string -> string)
+ """
+
+ RELEASE_NAME_FILE = "/etc/system-release"
+
+ class RPMinfoError(Exception):
+ """Exception for errors in rpm queries"""
+ pass
+
+ def get_python_opts():
+ """
+ Returns python's command line options
+
+ :return: list of python's command line options
+ :rtype: list
+
+ """
+
+ flags_to_opts = {
+ "debug": "-d",
+ "py3k_warning": "-3",
+ "division_new": "-Qnew",
+ "division_warning": "-Q",
+ "inspect": "-i",
+ "interactive": "-i",
+ "optimize": "-O",
+ "dont_write_bytecode": "-B",
+ "no_user_site": "-s",
+ "no_site": "-S",
+ "ignore_environment": "-E",
+ "tabcheck": "-t",
+ "verbose": "-v",
+ "unicode": "-u",
+ "bytes_warning": "-b",
+ "quiet": "-q",
+ }
+
+ ret = set()
+ for flag in flags_to_opts.keys():
+ if getattr(sys.flags, flag, None):
+ ret.add(flags_to_opts[flag])
+ ret = list(ret)
+ return ret
+
+ def get_package_and_component(file_=sys.argv[0]):
+ """
+ Returns package and component names for file (by default for script itself).
+
+ :param file_: filename
+ :type file_: string
+ :return: tuple containing package info and component name for the file
+ :rtype: (PackageInfo, str)
+ :raise RPMinfoError: if package and component for the file cannot be found
+
+ """
+
+ import rpm
+ ts = rpm.TransactionSet()
+ mi = ts.dbMatch("basenames", file_)
+ try:
+ header = next(mi)
+ except StopIteration:
+ raise RPMinfoError("Cannot get package and component for file "+
+ "{0}".format(file_))
+ pkg_info = PackageInfo(header["name"].decode("utf-8"), header["version"].decode("utf-8"),
+ header["release"].decode("utf-8"),
+ u"%d" % header["epoch"] if header["epoch"] else u"0",
+ header["arch"].decode("utf-8"))
+
+ # cuts the name from the NVR format: foo-blah-2.8.8-2.fc17.src.rpm
+ srpm_name = header["sourcerpm"].decode("utf-8")
+ name_end = len(srpm_name)
+ try:
+ name_end = srpm_name.rindex('-')
+ name_end = srpm_name[:name_end].rindex('-')
+ except ValueError:
+ # expected exception
+ pass
+
+ component = srpm_name[:name_end]
+
+ return (pkg_info, component)
+
+ def get_release_version():
+ """Returns release version (according to RELEASE_NAME_FILE)"""
+ try:
+ with open(RELEASE_NAME_FILE, "r") as file_:
+ return file_.readline().rstrip()
+ except IOError:
+ return "Cannot get release name."
+
+ def get_other_packages(self):
+ """
+ Returns a set of additional packages whose files appear in
+ traceback.
+
+ :rtype: set
+
+ """
+ packages = set()
+ if not self.stack:
+ return packages
+
+ for (_frame, fn, _lineno, _func, _ctx, _idx) in self.stack:
+ try:
+ pkg_info = get_package_and_component(fn)[0]
+ package = "{0.name}-{0.version}-{0.release}.{0.arch}".format(
+ pkg_info)
+ packages.add(package)
+ except RPMinfoError:
+ continue
+
+ try:
+ pkg_info = get_package_and_component()[0]
+ package = "{0.name}-{0.version}-{0.release}.{0.arch}".format(
+ pkg_info)
+ packages.discard(package)
+ except RPMinfoError:
+ pass
+ return packages
+
+ def get_environment_variables():
+ """
+ Returns a list of strings containing defined environment
+ variables and their values in the following format:
+
+ VARIABLE=VALUE
+
+ :rtype: list
+
+ """
+
+ ret = list()
+ for (key, value) in os.environ.items():
+ ret.append("{0}={1}".format(key, value))
+
+ return ret
+
+
+ #--begining of the method _get_environment_info--
+ try:
+ pkg_info, component = get_package_and_component()
+ except RPMinfoError:
+ pkg_info = None
+ component = None
+
+ other_packages = ", ".join(get_other_packages(self))
+
+ ret = dict()
+ ret["architecture"] = os.uname()[4]
+ ret["cmdline"] = "{0} {1} {2}".format(sys.executable,
+ " ".join(get_python_opts()),
+ " ".join(sys.argv))
+ if component:
+ ret["component"] = component
+ ret["executable"] = sys.argv[0]
+ ret["kernel"] = os.uname()[2]
+ if pkg_info:
+ ret["pkg_info"] = pkg_info
+ ret["release"] = get_release_version()
+ if other_packages:
+ ret["other involved packages"] = other_packages
+ ret["environ"] = "\n".join(get_environment_variables())
+
+ return ret
+
+ @property
+ def environment_info(self):
+ return self._get_environment_info()
+
+ def __str__(self):
+ lst = self._format_stack()
+
+ lst.insert(0, "%s %s exception report\n" % (self.conf.programName, self.conf.programVersion))
+ lst.insert(1, "Traceback (most recent call last):\n")
+
+ if self.type is not None and self.value is not None:
+ lst.extend(traceback.format_exception_only(self.type, self.value))
+
+ return "".join(lst)
+
+ def _format_stack(self):
+ if not self.stack:
+ return []
+
+ frames = []
+ for (_frame, fn, lineno, func, ctx, _idx) in self.stack:
+ if isinstance(ctx, list):
+ code = "".join(ctx)
+ else:
+ code = ctx
+
+ frames.append((fn, lineno, func, code))
+
+ return traceback.format_list(frames)
+
+ # Create a string representation of a class. This method will recursively
+ # handle all attributes of the base given class.
+ def _dumpClass(self, instance, level=0, parentkey="", skipList=None):
+ # This is horribly cheesy, and bound to require maintainence in the
+ # future. The problem is that anything subclassed from object fails
+ # the types.InstanceType test we used to have which means those
+ # objects never get dumped. However, most everything basic in
+ # python is derived from object so we can't just check for the super
+ # class. Any instances of the following types will just be printed
+ # out, and everything else will be assumed to be something that
+ # needs to be recursed on.
+ def __isSimpleType(instance):
+ return isinstance(instance, bool) or \
+ isinstance(instance, complex) or \
+ isinstance(instance, float) or \
+ isinstance(instance, int) or \
+ isinstance(instance, longtype) or \
+ isinstance(instance, type(None)) or \
+ isinstance(instance, bytes) or \
+ isinstance(instance, str) or \
+ isinstance(instance, unitype) or \
+ not hasattr(instance, "__class__") or \
+ not hasattr(instance, "__dict__")
+
+ ret = SafeStr()
+
+ if not skipList:
+ skipList = []
+
+ # protect from loops
+ try:
+ # Store the id(), not the instance name to protect against
+ # instances that cannot be hashed.
+ if not id(instance) in self._dumpHash:
+ self._dumpHash[id(instance)] = None
+ else:
+ ret += "Already dumped (%s instance)\n" % instance.__class__.__name__
+ return ret
+ except TypeError:
+ ret += "Cannot dump object\n"
+ return ret
+
+ if ("__str__" in instance.__class__.__dict__ or
+ "__repr__" in instance.__class__.__dict__):
+ try:
+ ret += "%s\n" % (instance,)
+ except:
+ ret += "\n"
+
+ return ret
+
+ ret += "%s instance, containing members:\n" %\
+ (instance.__class__.__name__)
+ pad = ' ' * ((level) * 2)
+
+ for key, value in instance.__dict__.items():
+ if key.startswith("_%s__" % instance.__class__.__name__):
+ continue
+
+ if parentkey != "":
+ curkey = parentkey + "." + key
+ else:
+ curkey = key
+
+ # Don't dump objects that are in our skip list, though ones that are
+ # None are probably okay.
+ if eval("instance.%s is not None" % key) and \
+ eval("id(instance.%s)" % key) in skipList:
+ ret += "%s%s: Skipped\n" % (pad, curkey)
+ continue
+
+ if isinstance(value, list):
+ ret += "%s%s: [" % (pad, curkey)
+ first = 1
+ for item in value:
+ if not first:
+ ret += ", "
+ else:
+ first = 0
+
+ if __isSimpleType(item):
+ ret += "%s" % (item,)
+ else:
+ ret += self._dumpClass(item, level + 1, skipList=skipList)
+ ret += "]\n"
+ elif isinstance(value, dict):
+ # append things one after another so that e.g. binary data is
+ # replaced by hexa values separately
+ ret += pad
+ ret += curkey
+ ret += ": {"
+ first = 1
+ for k, v in value.items():
+ if not first:
+ ret += ", "
+ else:
+ first = 0
+ if isinstance(k, str) or isinstance(k, unitype):
+ ret += "'"
+ ret += k
+ ret += "': "
+ else:
+ ret += "%s: " % (k,)
+
+ if __isSimpleType(v):
+ ret += "%s" % (v,)
+ else:
+ ret += self._dumpClass(v, level + 1, parentkey = curkey, skipList=skipList)
+ ret += "}\n"
+ elif __isSimpleType(value):
+ ret += "%s%s: " % (pad, curkey)
+ ret += value
+ ret += "\n"
+ else:
+ ret += "%s%s: " % (pad, curkey)
+ ret += self._dumpClass(value, level + 1, parentkey=curkey, skipList=skipList)
+
+ return ret
+
+ def dump(self, obj):
+ """Dump the local variables and all attributes of a given object.
+ The lists of files and attrs to ignore are all taken from a
+ Config object instance provided when the ExceptionDump class was
+ created.
+
+ obj -- Any Python object. This object will have all its attributes
+ written out, except for those mentioned in the attrSkipList.
+ """
+ idSkipList = []
+ ret = SafeStr()
+
+ # We need to augment the environment eval() will run in with the
+ # bindings that were local when the traceback happened so that the
+ # idSkipList can be populated. However since we're not allowed to
+ # modify the results of locals(), we'll have to make a copy first.
+ localVars = copy.copy(locals())
+ if self.stack:
+ localVars.update(self.stack[-1][0].f_locals)
+
+ # Catch attributes that do not exist at the time we do the exception dump
+ # and ignore them.
+ for k in self.conf.attrSkipList:
+ try:
+ eval("idSkipList.append(id(obj.%s))" % k, None, localVars)
+ except:
+ pass
+
+ # Write local variables to the given file descriptor, ignoring any of
+ # the local skips.
+ if self.stack:
+ frame = self.stack[-1][0]
+ ret += "\nLocal variables in innermost frame:\n"
+ try:
+ for (key, value) in frame.f_locals.items():
+ lowered_key = key.lower()
+ if any(item.lower() in lowered_key
+ for item in self.conf.localSkipList):
+ continue
+
+ ret += "%s: %s\n" % (key, value)
+ except:
+ pass
+
+ # And now dump the object's attributes.
+ try:
+ ret += "\n\n"
+ ret += self._dumpClass(obj, skipList=idSkipList)
+ except:
+ ret += "\nException occurred during state dump:\n"
+ ret += traceback.format_exc(None)
+
+ # Filter out item names and callbacks that should appear
+ # only as attachments
+ items_callbacks = ((name, cb) for (name, (cb, attchmnt_only))
+ in self.conf.callbackDict.items()
+ if not attchmnt_only)
+
+ # And now add data returned by the registered callbacks
+ ret += "\nRegistered callbacks:\n"
+ for (item_name, callback) in items_callbacks:
+ try:
+ callback_ret = callback()
+ if callback_ret:
+ ret += "%s:\n%s\n" % (item_name, callback_ret)
+ except Exception as exc:
+ ret += "%s: Caused error: %s\n" % (item_name, exc)
+
+ # And finally, write a bunch of files into the dump too.
+ for fname in self.conf.fileList:
+ try:
+ if six.PY2:
+ with codecs.open(fname, "r", "utf-8", "ignore") as fobj:
+ ret += "\n\n%s:\n" % (fname,)
+ for line in fobj:
+ ret += line.encode("utf-8")
+ else:
+ with open(fname, "rt", encoding="utf-8") as fobj:
+ ret += "\n\n%s:\n" % (fname,)
+ for line in fobj:
+ ret += line
+ except IOError:
+ pass
+ except:
+ ret += "\nException occurred during %s file copy:\n" % (fname,)
+ ret += traceback.format_exc(None)
+
+ return ret
+
+ @property
+ def hash(self):
+ """Create a hash for this traceback object. This is most suitable for
+ searching bug filing systems for duplicates. The hash is composed
+ of the basename of each file in the stack, the method names, and
+ the bit of code. Line numbers and the actual exception message
+ itself are left out.
+ """
+ import hashlib
+ s = ""
+
+ if self.stack:
+ for (file, _lineno, func, text) in [f[1:5] for f in self.stack]:
+ if isinstance(text, list):
+ text = "".join(text)
+ s += "%s %s %s\n" % (os.path.basename(file), func, text)
+ else:
+ s = "%s %s" % (self.type, self.value)
+
+ return hashlib.sha256(s.encode("utf-8")).hexdigest()
+
+ def traceback_and_object_dump(self, obj):
+ """
+ Return the traceback and a text representation of an object.
+
+ :param obj: Any Python object. This object will have all its attributes
+ written out, except for those mentioned in the attrSkipList.
+
+ """
+
+ ret = SafeStr(str(self))
+ ret += self.dump(obj)
+
+ return ret
+
+class ReverseExceptionDump(ExceptionDump):
+ """This class provides an alternate representation of an exception. In
+ this representation, the traceback is printed with the most recent call
+ at the top of the stack, and the most generic call at the bottom. Note
+ that this order does not affect the hash at all.
+ """
+ def __str__(self):
+ lst = self._format_stack()
+ lst.reverse()
+
+ lst.insert(0, "%s %s exception report\n" % (self.conf.programName, self.conf.programVersion))
+ lst.insert(1, "Traceback (most recent call first):\n")
+
+ if self.type is not None and self.value is not None:
+ lst.extend(traceback.format_exception_only(self.type, self.value))
+
+ return "".join(lst)
diff --git a/pkgbuilds/python-meh/pkg/python-meh/usr/lib/python3.14/site-packages/meh/handler.py b/pkgbuilds/python-meh/pkg/python-meh/usr/lib/python3.14/site-packages/meh/handler.py
new file mode 100644
index 0000000..6ae0a5d
--- /dev/null
+++ b/pkgbuilds/python-meh/pkg/python-meh/usr/lib/python3.14/site-packages/meh/handler.py
@@ -0,0 +1,299 @@
+# Copyright (C) 2009 Red Hat, Inc.
+# All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#
+# Author(s): Chris Lumens
+#
+from meh import MAIN_RESPONSE_DEBUG, MAIN_RESPONSE_SAVE, MAIN_RESPONSE_SHELL, MAIN_RESPONSE_QUIT, DumpInfo, ExceptionInfo
+import bdb
+import os
+import sys
+import report
+import traceback
+import subprocess
+
+import gettext
+_ = lambda x: gettext.ldgettext("python-meh", x)
+
+class NoNetwork(Exception):
+ def __init__(self, msg=""):
+ Exception.__init__(self)
+ self.msg = msg
+
+ def __str__(self):
+ return "No network is available"
+
+class ExceptionHandler(object):
+ def __init__(self, confObj, intfClass, exnClass):
+ """Create a new ExceptionHandler instance. Subclasses of this one may
+ be created, provided they are careful to provide all the methods
+ this one does. Instance attributes:
+
+ conf -- A filled in Config instance. At least the programName
+ and programVersion are required.
+ exnClass -- An instance of ExceptionDump or a subclass of it. This
+ is required to know how to represent the Python
+ exception internally.
+ intfClass-- An instance of AbstractIntf. This is required to know
+ what UI classes to use.
+ """
+ self.conf = confObj
+ self.exnClass = exnClass
+ self.intf = intfClass
+
+ self._exitcode = 10
+ self._exn = None
+ self.exnFile = None
+ self.exnText = ""
+
+ def _setExitCode(self, code):
+ self._exitcode = code
+
+ exitcode = property(lambda s: s._exitcode,
+ lambda s, v: s._setExitCode(v))
+
+ def _setExn(self, e):
+ self._exn = e
+
+ exn = property(lambda s: s._exn,
+ lambda s, v: s._setExn(v))
+
+ def handleException(self, dump_info):
+ """This is the main exception handling entry point. When Python
+ gets an exception it doesn't know how to handle, this method will
+ be called. It then saves the traceback and displays the main
+ dialog asking the user what to do next. Once this method is
+ called, there's no good way to go back to what you were doing
+ before.
+
+ All arguments are passed in from the handler created by calling
+ self.install(). This method should not usually be overridden by
+ a subclass.
+
+ :type dump_info: an instance of the meh.DumpInfo class
+
+ """
+
+ responseHash = { MAIN_RESPONSE_QUIT: self.runQuit,
+ MAIN_RESPONSE_DEBUG: self.runDebug,
+ MAIN_RESPONSE_SAVE: self.runSave,
+ MAIN_RESPONSE_SHELL: self.runShell,
+ }
+
+ # Quit if we got an exception when running pdb.
+ if isinstance(dump_info.exc_info.value, bdb.BdbQuit):
+ sys.exit(self.exitcode)
+
+ # Restore original exception handler.
+ sys.excepthook = sys.__excepthook__
+
+ self.preWriteHook(dump_info)
+
+ # Save the exception to the filesystem first.
+ self.exn = self.exnClass(dump_info.exc_info, self.conf)
+ (fobj, self.exnFile) = self.openFile()
+ self.exnText = self.exn.traceback_and_object_dump(dump_info.object)
+ fobj.write(self.exnText)
+ fobj.close()
+
+ self.postWriteHook(dump_info)
+
+ # Run initial UI screen, asking whether to save/debug/quit.
+ while True:
+ win = self.intf.mainExceptionWindow(str(self.exn), self.exnText)
+ if not win:
+ self.runQuit(dump_info.exc_info)
+
+ rc = win.run()
+ win.destroy()
+
+ try:
+ responseHash[rc](dump_info.exc_info)
+ except KeyError:
+ # This happens if (for example) the user hits Escape instead
+ # of pressing a button.
+ continue
+
+ win.destroy()
+
+ def preWriteHook(self, dump_info):
+ """Subclasses may supply a function with this name that will be
+ called immediately before the traceback is written to disk in
+ order to have any sort of special pre-write processing that needs
+ to be done.
+
+ :type dump_info: an instance of the meh.DumpInfo class
+ """
+ pass
+
+ def postWriteHook(self, dump_info):
+ """Subclasses may supply a function with this name that will be
+ called immediately after the traceback is written to disk, but
+ immediately before the UI is run. This is to provide a place for
+ any special handling to happen once there is a file on disk.
+
+ :type dump_info: an instance of the meh.DumpInfo class
+ """
+ pass
+
+ def install(self, obj):
+ """Install ourselves as the top level exception handler with Python.
+ If this method is not called after an ExceptionHandler instance is
+ created, none of the rest of this code will ever be called.
+
+ obj -- A Python object that may be dumped to a file when the
+ exception is saved. This should be something like the top
+ level object in a program.
+ """
+ sys.excepthook = lambda ty, value, tb: \
+ self.handleException(DumpInfo(ExceptionInfo(ty, value, tb), obj))
+
+ def openFile(self):
+ """Create a randomly named output file to write the exception dump to.
+ This requires a programName be set in the Config instance. The
+ return value is a (file descriptor, path) pair. The file must be
+ closed by the caller when writing is done. Subclasses should not
+ override this method unless they know what they're doing.
+ """
+ import tempfile
+ (fd, path) = tempfile.mkstemp("", "%s-tb-" % self.conf.programName, "/tmp")
+ fo = os.fdopen(fd, "w")
+ return (fo, path)
+
+ ### Methods called by the primary exception handling dialog.
+
+ def runQuit(self, exc_info):
+ """This method is called when the "Exit" button is clicked. It may
+ be overridden by a subclass, but the basic behavior of eventually
+ quitting should be preserved.
+
+ :type exc_info: an instance of the meh.ExceptionInfo class
+ """
+ sys.exit(self.exitcode)
+
+ def runDebug(self, exc_info):
+ """This method is called when the "Debug" button is clicked. It may
+ be overridden by a subclass if specialized behavior is required to
+ enter debug mode.
+
+ :type exc_info: an instance of the meh.ExceptionInfo class
+ """
+
+ print("")
+ print(_("Use 'continue' command to quit the debugger and get back to "
+ "the main menu"))
+ import pdb
+ pdb.post_mortem(exc_info.stack)
+ #no need to quit here, let's just get back to the main dialog
+
+ def runShell(self, exc_info):
+ """This method is called when user requests to run a shell. It may
+ be overridden by a subclass if specialized behavior is required to
+ run shell (e.g. TTY switching).
+
+ :type exc_info: an instance of the meh.ExceptionInfo class
+
+ """
+
+ print("")
+ print(_("Exit the shell to get back to the main menu"))
+ proc = subprocess.Popen(["bash", "--login"], shell=True, cwd="/")
+ proc.wait()
+
+ def runSave(self, exc_info):
+ """This method is called when the "Save" button is clicked. It may
+ be overridden by a subclass, but that's going to be a lot of work.
+
+ :type exc_info: an instance of the meh.ExceptionInfo class
+ """
+
+ params = dict()
+ params.update(self.exn.environment_info)
+ pkg_info = params.pop("pkg_info", None)
+
+ #if rpmdb queries fail (anaconda), use self.conf.* values
+ if "component" not in params:
+ params["component"] = self.conf.programName
+
+ if pkg_info:
+ params["pkg_name"] = pkg_info.name
+ params["pkg_version"] = pkg_info.version
+ params["pkg_release"] = pkg_info.release
+ params["pkg_epoch"] = pkg_info.epoch
+ params["pkg_arch"] = pkg_info.arch
+ params["package"] = "%s-%s-%s.%s" % (pkg_info.name, pkg_info.version,
+ pkg_info.release, pkg_info.arch)
+ else:
+ params["pkg_name"] = self.conf.programName
+ parts = self.conf.programVersion.rsplit("-", 1)
+ if len(parts) == 2:
+ version, release = parts
+ else:
+ version = parts[0]
+ # not given, make something up
+ release = "1"
+ params["pkg_version"] = version
+ params["pkg_release"] = release
+ # we don't support specifying epoch in the config object and only
+ # the anaconda which is not using epochs doesn't have the RPMdb to
+ # query
+ params["pkg_epoch"] = "0"
+ params["pkg_arch"] = self.conf.programArch
+ params["package"] = "{0.programName}-{0.programVersion}".format(
+ self.conf)
+
+ params["hashmarkername"] = self.conf.programName
+ params["duphash"] = self.exn.hash
+ params["reason"] = self.exn.desc
+ params["description"] = "The following was filed automatically by %s:\n%s" \
+ % (self.conf.programName, str(self.exn))
+
+ tb_item_name = "%s-tb" % self.conf.programName
+ params[tb_item_name] = self.exnText
+
+ # save data generated by callbacks
+ for (item_name, (callback, _anc)) in self.conf.callbackDict.items():
+ try:
+ ret = callback()
+ if ret:
+ params[item_name] = ret
+ except Exception as exc: # pylint: disable=broad-except
+ params[item_name] = "Caused error: %s" % exc
+
+ for fpath in self.conf.fileList:
+ try:
+ with open(fpath, "r") as fobj:
+ # would be better to use the full path here, but libreport
+ # doesn't allow '/' characters in the item name
+ filename = os.path.basename(fpath)
+ if filename not in params:
+ params[filename] = fobj.read()
+ else:
+ params[filename+"_file"] = fobj.read()
+ except: # pylint: disable=bare-except
+ #skip files we cannot read
+ continue
+
+ # tell ABRT some more things it requires
+ params["analyzer"] = "Python"
+ params["backtrace"] = "".join(traceback.format_exception(exc_info.type,
+ exc_info.value,
+ exc_info.stack))
+
+ signature = report.createPythonUnhandledExceptionSignature(**params)
+
+ # We don't want to automatically quit here since the user may wish to
+ # save somewhere else, debug, etc.
+ self.intf.saveExceptionWindow(signature)
diff --git a/pkgbuilds/python-meh/pkg/python-meh/usr/lib/python3.14/site-packages/meh/network.py b/pkgbuilds/python-meh/pkg/python-meh/usr/lib/python3.14/site-packages/meh/network.py
new file mode 100644
index 0000000..ede2b8a
--- /dev/null
+++ b/pkgbuilds/python-meh/pkg/python-meh/usr/lib/python3.14/site-packages/meh/network.py
@@ -0,0 +1,45 @@
+#
+# network.py - network configuration install data
+#
+# Copyright (C) 2008, 2009 Red Hat, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#
+# Author(s): David Cantrell
+#
+import dbus
+
+# This code was taken from anaconda, and slightly adapted to not rely
+# on isys.
+NM_SERVICE = "org.freedesktop.NetworkManager"
+NM_MANAGER_PATH = "/org/freedesktop/NetworkManager"
+
+NM_STATE_CONNECTED = 3
+
+DBUS_PROPS_IFACE = "org.freedesktop.DBus.Properties"
+
+def hasActiveNetDev():
+ """Does the system have an enabled network interface?"""
+ try:
+ bus = dbus.SystemBus()
+ nm = bus.get_object(NM_SERVICE, NM_MANAGER_PATH)
+ props = dbus.Interface(nm, DBUS_PROPS_IFACE)
+ state = props.Get(NM_SERVICE, "State")
+
+ if int(state) == NM_STATE_CONNECTED:
+ return True
+ else:
+ return False
+ except dbus.DBusException:
+ return False
diff --git a/pkgbuilds/python-meh/pkg/python-meh/usr/lib/python3.14/site-packages/meh/safe_string.py b/pkgbuilds/python-meh/pkg/python-meh/usr/lib/python3.14/site-packages/meh/safe_string.py
new file mode 100644
index 0000000..714ce37
--- /dev/null
+++ b/pkgbuilds/python-meh/pkg/python-meh/usr/lib/python3.14/site-packages/meh/safe_string.py
@@ -0,0 +1,71 @@
+#
+# Copyright (C) 2013 Red Hat, Inc.
+# All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#
+# Author: Vratislav Podzimek
+#
+#
+
+import sys
+PY = int(sys.version.split('.')[0])
+
+"""
+This module provides a SafeStr class.
+
+@see: SafeStr
+
+"""
+
+class SafeStr(str):
+ """
+ String class that has a modified __add__ method so that ascii strings,
+ binary data represented as a byte string and unicode objects can be
+ safely appended to it (not causing traceback). BINARY DATA IS OMITTED.
+
+ """
+
+ def __add__(self, other):
+
+ if PY > 2:
+ return SafeStr(str.__add__(self, str(other)))
+
+ if not (isinstance(other, str) or isinstance(other, unicode)): # pylint: disable=undefined-variable
+ if hasattr(other, "__str__"):
+ other = other.__str__()
+ else:
+ other = "OMITTED OBJECT WITHOUT __str__ METHOD"
+
+ if isinstance(other, unicode): # pylint: disable=undefined-variable
+ ret = SafeStr(str.__add__(self, other.encode("utf-8")))
+ else:
+ try:
+ # try to decode which doesn't cause traceback for utf-8 encoded
+ # non-ascii string and ascii string
+ other.decode("utf-8")
+ ret = SafeStr(str.__add__(self, other))
+ except UnicodeDecodeError:
+ # binary data, get the representation used by Python for
+ # non-ascii bytes
+
+ # hex(255) returns "0xff", we want "\xff"
+ other_hexa = (hex(ord(char)) for char in other)
+ other_backslashed = (hex_num.replace("0x", "\\x")
+ for hex_num in other_hexa)
+ other_repr = "".join(other_backslashed)
+
+ ret = SafeStr(str.__add__(self, other_repr))
+
+ return ret
diff --git a/pkgbuilds/python-meh/pkg/python-meh/usr/lib/python3.14/site-packages/meh/ui/__init__.py b/pkgbuilds/python-meh/pkg/python-meh/usr/lib/python3.14/site-packages/meh/ui/__init__.py
new file mode 100644
index 0000000..8c174e1
--- /dev/null
+++ b/pkgbuilds/python-meh/pkg/python-meh/usr/lib/python3.14/site-packages/meh/ui/__init__.py
@@ -0,0 +1,181 @@
+# Copyright (C) 2009 Red Hat, Inc.
+# All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#
+# Author(s): Chris Lumens
+#
+
+class AbstractIntf(object):
+ """The base class for Interfaces. This is an abstract class. It
+ provides methods that create and run various kinds of windows.
+ We provide a basic level of graphical and text oriented interfaces
+ here, but it should be possible for things like anaconda to supply
+ their own much more complicated ones with little effort.
+ """
+ def __init__(self, *args, **kwargs):
+ """Create a new AbstractInterface instance. This method need not
+ be overridden by subclasses.
+ """
+ pass
+
+ def enableNetwork(self, *args, **kwargs):
+ """Display and run a dialog that brings up the network, if it is
+ currently disabled. This method must be provided by all subclasses,
+ and should return False if unsuccessful.
+ """
+ raise NotImplementedError
+
+ def exitWindow(self, title, message, *args, **kwargs):
+ """Display and run a dialog that displays a message with the proper
+ buttons and images for quitting afterwards. This method must be
+ provided by all subclasses.
+
+ title -- The window's title.
+ message -- The window's text.
+ """
+ raise NotImplementedError
+
+ def mainExceptionWindow(self, text, exnFile, *args, **kwargs):
+ """Create and return an instance of the initial execption handling
+ dialog. This method must be provided by all subclasses.
+
+ text -- The short traceback (usually, just the stack trace).
+ exnFile -- A file containing the output of ExceptionDump.write().
+ """
+ raise NotImplementedError
+
+ def messageWindow(self, title, message, *args, **kwargs):
+ """Display a generic informational message. This can be an error,
+ status update, and so forth. It must be provided by all subclasses.
+
+ title -- The window's title.
+ message -- The window's text.
+ """
+ raise NotImplementedError
+
+ def saveExceptionWindow(self, exnFile, *args, **kwargs):
+ """Create and return an instance of the exception saving dialog. This
+ method must be provided by all subclasses.
+ """
+ raise NotImplementedError
+
+class AbstractMainExceptionWindow(object):
+ """This abstract class describes the basic level of support required of
+ any interface that supplies a main exception window. The main exception
+ window is the initial dialog displayed by the exception handler that
+ provides the debug/save/quit options.
+ """
+ def __init__(self, shortTraceback=None, longTraceback=None, *args, **kwargs):
+ """Create a new MainExceptionWindow instance. This must handle
+ creating the dialog and populating it with widgets, but must not
+ run the dialog. A self.dialog attribute should be created that
+ refers to the dialog itself.
+
+ shortTraceback -- The short traceback (usually, just the stack
+ trace).
+ longTraceback -- The long traceback (full traceback and the main
+ object dump).
+ """
+ pass
+
+ def destroy(self, *args, **kwargs):
+ """Destroy the current dialog. This method must be provided by all
+ subclasses.
+ """
+ raise NotImplementedError
+
+ def run(self, *args, **kwargs):
+ """Run the window and set a return value. This method does everything
+ after the interface has been set up, but does not destroy it. This
+ method must be provided by all subclasses.
+ """
+ raise NotImplementedError
+
+class AbstractExitWindow(object):
+ """This abstract class describes the basic level of support required of
+ a basic message dialog that should be displayed before the program
+ exits. All interfaces must provide this class.
+ """
+ def __init__(self, title, text, *args, **kwargs):
+ """Create a new ExitWindow instance. This must handle creating the
+ dialog and putting the text into it, but must not run the dialog.
+ This method must be provided by all subclasses.
+
+ title -- The window's title.
+ text -- The window's text.
+ """
+ pass
+
+ def destroy(self, *args, **kwargs):
+ """Destroy the current dialog. This method must be provided by all
+ subclasses.
+ """
+ raise NotImplementedError
+
+ def run(self, *args, **kwargs):
+ """Run the window and set a return value. This method does everything
+ after the interface has been set up but does not destroy it. This
+ method must be provided by all subclasses.
+ """
+ raise NotImplementedError
+
+class AbstractMessageWindow(object):
+ """This abstract class describes the basic level of support required of
+ a basic message dialog. All interfaces must provide this class.
+ """
+ def __init__(self, title, text, *args, **kwargs):
+ """Create a new MessageWindow instance. This must handle creating the
+ dialog and putting the text into it, but must not run the dialog.
+ This method must be provided by all subclasses.
+
+ title -- The window's title.
+ text -- The window's text.
+ """
+ pass
+
+ def destroy(self, *args, **kwargs):
+ """Destroy the current dialog. This method must be provided by all
+ subclasses.
+ """
+ raise NotImplementedError
+
+ def run(self, *args, **kwargs):
+ """Run the window and set a return value. This method does everything
+ after the interface has been set up but does not destroy it. This
+ method must be provided by all subclasses.
+ """
+ raise NotImplementedError
+
+class AbstractSaveExceptionWindow(object):
+ """This abstract class describes the basic level of support required of
+ any interface that supplies an exception saving window. The exception
+ saving window is the dialog displayed if the user clicks "save" on the
+ initial dialog, and presents multiple options for how the traceback
+ should be saved.
+ """
+ def __init__(self, *args, **kwargs):
+ """Create a new SaveExceptionWindow instance. This must handle
+ creating the dialog and populating it with widgets, but must not
+ run the dialog. A self.dialog attribute should be created that
+ refers to the dialog itself.
+ """
+ pass
+
+ def run(self, *args, **kwargs):
+ """Run the window and set a return value. This method does everything
+ after the interface has been set up but does not destroy it. This
+ method must be provided by all subclasses.
+ """
+ raise NotImplementedError
diff --git a/pkgbuilds/python-meh/pkg/python-meh/usr/lib/python3.14/site-packages/meh/ui/__pycache__/__init__.cpython-314.pyc b/pkgbuilds/python-meh/pkg/python-meh/usr/lib/python3.14/site-packages/meh/ui/__pycache__/__init__.cpython-314.pyc
new file mode 100644
index 0000000..39d9732
Binary files /dev/null and b/pkgbuilds/python-meh/pkg/python-meh/usr/lib/python3.14/site-packages/meh/ui/__pycache__/__init__.cpython-314.pyc differ
diff --git a/pkgbuilds/python-meh/pkg/python-meh/usr/lib/python3.14/site-packages/meh/ui/__pycache__/gui.cpython-314.pyc b/pkgbuilds/python-meh/pkg/python-meh/usr/lib/python3.14/site-packages/meh/ui/__pycache__/gui.cpython-314.pyc
new file mode 100644
index 0000000..9cae43b
Binary files /dev/null and b/pkgbuilds/python-meh/pkg/python-meh/usr/lib/python3.14/site-packages/meh/ui/__pycache__/gui.cpython-314.pyc differ
diff --git a/pkgbuilds/python-meh/pkg/python-meh/usr/lib/python3.14/site-packages/meh/ui/__pycache__/text.cpython-314.pyc b/pkgbuilds/python-meh/pkg/python-meh/usr/lib/python3.14/site-packages/meh/ui/__pycache__/text.cpython-314.pyc
new file mode 100644
index 0000000..726a777
Binary files /dev/null and b/pkgbuilds/python-meh/pkg/python-meh/usr/lib/python3.14/site-packages/meh/ui/__pycache__/text.cpython-314.pyc differ
diff --git a/pkgbuilds/python-meh/pkg/python-meh/usr/lib/python3.14/site-packages/meh/ui/gui.py b/pkgbuilds/python-meh/pkg/python-meh/usr/lib/python3.14/site-packages/meh/ui/gui.py
new file mode 100644
index 0000000..b6920a3
--- /dev/null
+++ b/pkgbuilds/python-meh/pkg/python-meh/usr/lib/python3.14/site-packages/meh/ui/gui.py
@@ -0,0 +1,177 @@
+# Copyright (C) 2009 Red Hat, Inc.
+# All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#
+# Author(s): Chris Lumens
+#
+from meh import MAIN_RESPONSE_DEBUG, MAIN_RESPONSE_NONE, MAIN_RESPONSE_QUIT, MAIN_RESPONSE_SAVE
+from meh.ui import AbstractIntf, AbstractSaveExceptionWindow, AbstractMainExceptionWindow, AbstractMessageWindow
+import os
+import sys
+import report
+
+import gi
+gi.require_version("Gtk", "3.0")
+
+from gi.repository import Gtk
+
+import gettext
+_ = lambda x: gettext.ldgettext("python-meh", x)
+
+def find_glade_file(file):
+ path = os.environ.get("GLADEPATH", "./:ui/:/tmp/updates/:/tmp/updates/ui/:/usr/share/python-meh/")
+ for d in path.split(":"):
+ fn = d + file
+ if os.access(fn, os.R_OK):
+ return fn
+ raise RuntimeError("Unable to find glade file %s" % file)
+
+class GraphicalIntf(AbstractIntf):
+ def __init__(self, *args, **kwargs):
+ AbstractIntf.__init__(self, *args, **kwargs)
+
+ def enableNetwork(self, *args, **kwargs):
+ """Should be provided by the inheriting class."""
+
+ return False
+
+ def exitWindow(self, title, message, *args, **kwargs):
+ win = ExitWindow(title, message, *args, **kwargs)
+ win.run()
+ win.destroy()
+
+ def mainExceptionWindow(self, text, exnFile, *args, **kwargs):
+ win = MainExceptionWindow(text, exnFile, *args, **kwargs)
+ return win
+
+ def messageWindow(self, title, message, *args, **kwargs):
+ win = MessageWindow(title, message, *args, **kwargs)
+ win.run()
+ win.destroy()
+
+ def saveExceptionWindow(self, signature, *args, **kwargs):
+ win = SaveExceptionWindow(signature)
+ win.run()
+
+class SaveExceptionWindow(AbstractSaveExceptionWindow):
+ def __init__(self, signature, *args, **kwargs):
+ AbstractSaveExceptionWindow.__init__(self, signature, *args, **kwargs)
+ self.signature = signature
+
+ def run(self, *args, **kwargs):
+ # Don't need to check the return value of report since it will
+ # handle all the UI reporting for us.
+ report.report_problem_in_memory(self.signature, report.LIBREPORT_WAIT)
+
+class MainExceptionWindow(AbstractMainExceptionWindow):
+ def __init__(self, shortTraceback=None, longTraceback=None, *args, **kwargs):
+ AbstractMainExceptionWindow.__init__(self, shortTraceback, longTraceback,
+ *args, **kwargs)
+
+ builder = Gtk.Builder()
+ builder.set_translation_domain("python-meh")
+ glade_file = find_glade_file("exception-dialog.glade")
+ builder.add_from_file(glade_file)
+ builder.connect_signals(self)
+
+ self._main_window = builder.get_object("exceptionWindow")
+
+ # this makes sure the Meh window remains active
+ # even if the exception has been triggered in a dialog
+ window_group = Gtk.WindowGroup()
+ window_group.add_window(self._main_window)
+
+ self._traceback_buffer = builder.get_object("tracebackBuffer")
+
+ self._traceback_buffer.set_text(longTraceback)
+ self._response = MAIN_RESPONSE_QUIT
+
+ self._debug_button = builder.get_object("debugButton")
+
+ allowDebug = kwargs.get("allowDebug", sys.stdout.isatty)
+
+ if allowDebug and not allowDebug():
+ self._debug_button.set_sensitive(False)
+
+ @property
+ def main_window(self):
+ return self._main_window
+
+ def destroy(self, *args, **kwargs):
+ self._main_window.destroy()
+
+ def run(self, *args, **kwargs):
+ self._main_window.show_all()
+ # keep our window above dialogs
+ self._main_window.set_modal(True)
+ self._main_window.set_keep_above(True)
+ Gtk.main()
+ self.destroy()
+ return self._response
+
+ def on_report_clicked(self, button):
+ # hide our window to not interfere with libreport window
+ self._main_window.hide()
+ self._response = MAIN_RESPONSE_SAVE
+ Gtk.main_quit()
+
+ def on_quit_clicked(self, button):
+ self._response = MAIN_RESPONSE_QUIT
+ Gtk.main_quit()
+
+ def on_debug_clicked(self, button):
+ self._response = MAIN_RESPONSE_DEBUG
+ Gtk.main_quit()
+
+ def on_expander_activated(self, expander, *args):
+ if not expander.get_expanded():
+ self._main_window.resize(600, 400)
+ else:
+ #resize the window back to the default size when expander is
+ #minimized
+ self._main_window.resize(600, 1)
+
+ def on_main_window_deleted(self, *args):
+ self._response = MAIN_RESPONSE_NONE
+ self.destroy()
+ Gtk.main_quit()
+
+class MessageWindow(AbstractMessageWindow):
+ def __init__(self, title, text, *args, **kwargs):
+ AbstractMessageWindow.__init__(self, title, text, *args, **kwargs)
+ self.rc = 0
+
+ self.dialog = Gtk.MessageDialog(buttons=Gtk.ButtonsType.OK,
+ type=Gtk.MessageType.INFO,
+ message_format=text)
+ self.dialog.set_title(title)
+ self.dialog.set_position(Gtk.WindowPosition.CENTER)
+
+ def destroy(self, *args, **kwargs):
+ self.dialog.destroy()
+
+ def run(self, *args, **kwargs):
+ self.dialog.show_all()
+ self.rc = self.dialog.run()
+ self.dialog.destroy()
+
+class ExitWindow(MessageWindow):
+ def __init__(self, title, text, *args, **kwargs): # pylint: disable=super-init-not-called
+ self.dialog = Gtk.MessageDialog(buttons=Gtk.ButtonsType.NONE,
+ type=Gtk.MessageType.INFO,
+ message_format=text)
+ self.dialog.set_title(title)
+ self.dialog.add_button(_("_Exit"), 0)
+ self.dialog.set_position(Gtk.WindowPosition.CENTER)
diff --git a/pkgbuilds/python-meh/pkg/python-meh/usr/lib/python3.14/site-packages/meh/ui/text.py b/pkgbuilds/python-meh/pkg/python-meh/usr/lib/python3.14/site-packages/meh/ui/text.py
new file mode 100644
index 0000000..e4d99f2
--- /dev/null
+++ b/pkgbuilds/python-meh/pkg/python-meh/usr/lib/python3.14/site-packages/meh/ui/text.py
@@ -0,0 +1,225 @@
+# Copyright (C) 2009, 2013 Red Hat, Inc.
+# All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#
+# Author(s): Chris Lumens
+# Vratislav Podzimek
+#
+from __future__ import print_function
+
+from meh import MAIN_RESPONSE_DEBUG, MAIN_RESPONSE_SAVE, MAIN_RESPONSE_SHELL, MAIN_RESPONSE_QUIT
+from meh.ui import AbstractIntf, AbstractSaveExceptionWindow, AbstractMainExceptionWindow, AbstractMessageWindow
+import report
+import report.io.TextIO
+from report import LIBREPORT_WAIT, LIBREPORT_RUN_CLI
+
+import os
+import sys
+if sys.version_info.major == 3:
+ raw_input_fn = input
+else:
+ raw_input_fn = raw_input # pylint: disable=undefined-variable
+
+import gettext
+_ = lambda x: gettext.ldgettext("python-meh", x)
+
+
+class IOHandler(object):
+ """
+ Class that provides methods for input and output. Its instance is expected
+ to be passed to objects performing some I/O operations.
+
+ """
+
+ def __init__(self, in_func=raw_input_fn, out_func=print): # pylint: disable=used-before-assignment
+ """
+ Constructor for the IOhandler class. Arguments can be used to override
+ default I/O functions with the custom ones.
+
+ :param in_func: input function similar to standard raw_input
+ :type in_func: str -> str
+ :param out_func: output function similar to standard print
+ :type out_func: str -> None
+
+ """
+
+ self.in_func = in_func
+ self.out_func = out_func
+
+ def print(self, msg=""):
+ self.out_func(msg)
+
+ def raw_input(self, prompt):
+ return self.in_func(prompt)
+
+class TextWindow(object):
+ """Helper class providing some common methods needed by all text windows."""
+
+ def __init__(self, title, *args, **kwargs):
+ self._io = kwargs.get("io_handler", IOHandler())
+ self._title = title
+
+ # short (one-char) answer meaning "yes"
+ self._yes_answer = _("y")
+
+ # short (one-char) answer meaning "no"
+ self._no_answer = _("n")
+
+ @property
+ def _usable_width(self):
+ return os.environ.get("COLUMNS", 80) - 1
+
+ def _print_rule(self):
+ rule = self._usable_width * "="
+ self._io.print(rule)
+
+ def print_header(self):
+ self._io.print()
+ self._print_rule()
+ self._io.print(self._title)
+ self._print_rule()
+
+ def destroy(self):
+ self._print_rule()
+ self._print_rule()
+
+class TextIntf(AbstractIntf):
+ def __init__(self, *args, **kwargs):
+ AbstractIntf.__init__(self, *args, **kwargs)
+ self.screen = kwargs.get("screen", None)
+ self._io = kwargs.get("io_handler", IOHandler())
+
+ def set_io_handler(self, handler):
+ """
+ Set different IO handler.
+
+ :type handler: an instance of the IOHandler class
+
+ """
+ self._io = handler
+
+ def enableNetwork(self, *args, **kwargs):
+ """Should be provided by the inheriting class."""
+
+ return False
+
+ def exitWindow(self, title, message, *args, **kwargs):
+ kwargs["io_handler"] = self._io
+ win = ExitWindow(title, message, *args, **kwargs)
+ win.run()
+ win.destroy()
+
+ def mainExceptionWindow(self, text, exnFile, *args, **kwargs):
+ kwargs["io_handler"] = self._io
+ win = MainExceptionWindow(text, exnFile, *args, **kwargs)
+ return win
+
+ def messageWindow(self, title, message, *args, **kwargs):
+ kwargs["io_handler"] = self._io
+ win = MessageWindow(title, message, *args, **kwargs)
+ win.run()
+ win.destroy()
+
+ def saveExceptionWindow(self, signature, *args, **kwargs):
+ kwargs["io_handler"] = self._io
+ win = SaveExceptionWindow(signature, *args, **kwargs)
+ win.run()
+ win.destroy()
+
+class SaveExceptionWindow(TextWindow, AbstractSaveExceptionWindow):
+ def __init__(self, signature, *args, **kwargs):
+ AbstractSaveExceptionWindow.__init__(self, signature,
+ *args, **kwargs)
+ TextWindow.__init__(self, _("Save exception"), *args, **kwargs)
+ self.signature = signature
+
+ def run(self, *args, **kwargs):
+ # Don't need to check the return value of report since it will
+ # handle all the UI reporting for us.
+ report.report_problem_in_memory(self.signature,
+ LIBREPORT_WAIT|LIBREPORT_RUN_CLI)
+
+class MainExceptionWindow(TextWindow, AbstractMainExceptionWindow):
+ def __init__(self, shortTraceback=None, longTraceback=None, *args, **kwargs):
+ AbstractMainExceptionWindow.__init__(self, shortTraceback, longTraceback,
+ *args, **kwargs)
+ TextWindow.__init__(self, _("An unknown error has occurred"),
+ *args, **kwargs)
+ self._short_traceback = shortTraceback
+ self._menu_items = [(_("Report Bug"), MAIN_RESPONSE_SAVE),
+ (_("Run shell"), MAIN_RESPONSE_SHELL),
+ (_("Quit"), MAIN_RESPONSE_QUIT)]
+
+ allowDebug = kwargs.get("allowDebug", sys.stdout.isatty)
+
+ if allowDebug and allowDebug():
+ self._menu_items.insert(1, (_("Debug"), MAIN_RESPONSE_DEBUG))
+
+ def run(self, *args, **kwargs):
+ self.print_header()
+ self._io.print(self._short_traceback)
+ self._io.print(_("What do you want to do now?"))
+ for (idx, item) in enumerate(self._menu_items):
+ self._io.print("%d) %s" % (idx + 1, item[0]))
+
+ ret = -1
+ num_menu_items = len(self._menu_items)
+ self._io.print()
+ while not (0 < ret <= num_menu_items):
+ ret = self._io.raw_input(_("Please make your choice from above: "))
+ try:
+ ret = int(ret)
+ except ValueError:
+ ret = -1
+
+ return self._menu_items[ret - 1][1]
+
+class MessageWindow(TextWindow, AbstractMessageWindow):
+ def __init__(self, title, text, *args, **kwargs):
+ AbstractMessageWindow.__init__(self, title, text, *args, **kwargs)
+ TextWindow.__init__(self, title, *args, **kwargs)
+ self._text = text
+
+ def run(self, *args, **kwargs):
+ self.print_header()
+ self._io.print(self._text)
+ self._io.print()
+ self._io.raw_input(_("Hit ENTER to continue"))
+
+class ExitWindow(MessageWindow):
+ def __init__(self, title, text, *args, **kwargs):
+ MessageWindow.__init__(self, title, text, *args, **kwargs)
+
+ def run(self, *args, **kwargs):
+ self.print_header()
+ self._io.print(self._text)
+ self._io.print()
+
+ # self._no_answer may be non-ascii string (simple .upper() doesn't work)
+ no_answer_upper = self._no_answer.decode("utf-8").upper().encode("utf-8")
+ answer = self._io.raw_input(_(
+ "Are you sure you want to exit? [%(yes)s/%(no)s]") %
+ { "yes": self._yes_answer,
+ "no": no_answer_upper })
+
+ # no answer means accepting the default (self._no_answer) and the answer
+ # is case insensitive (and may be non-ascii)
+ lower_answer = answer.decode("utf-8").lower().encode("utf-8")
+ answer = lower_answer or self._no_answer
+
+ if answer in (self._yes_answer, self._no_answer):
+ return answer == self._yes_answer
+ else:
+ self.run(*args, **kwargs)
diff --git a/pkgbuilds/python-meh/pkg/python-meh/usr/share/licenses/python-meh/COPYING b/pkgbuilds/python-meh/pkg/python-meh/usr/share/licenses/python-meh/COPYING
new file mode 100644
index 0000000..a43ea21
--- /dev/null
+++ b/pkgbuilds/python-meh/pkg/python-meh/usr/share/licenses/python-meh/COPYING
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ Appendix: How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C) 19yy
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ , 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/pkgbuilds/python-meh/pkg/python-meh/usr/share/python-meh/exception-dialog.glade b/pkgbuilds/python-meh/pkg/python-meh/usr/share/python-meh/exception-dialog.glade
new file mode 100644
index 0000000..0bed126
--- /dev/null
+++ b/pkgbuilds/python-meh/pkg/python-meh/usr/share/python-meh/exception-dialog.glade
@@ -0,0 +1,248 @@
+
+
+
+
+
+ No additional info available.
+
+
diff --git a/pkgbuilds/python-meh/python-meh-0.43-1-any.pkg.tar.zst b/pkgbuilds/python-meh/python-meh-0.43-1-any.pkg.tar.zst
new file mode 100644
index 0000000..2f47ed1
Binary files /dev/null and b/pkgbuilds/python-meh/python-meh-0.43-1-any.pkg.tar.zst differ
diff --git a/pkgbuilds/python-meh/python-meh-0.43.tar.gz b/pkgbuilds/python-meh/python-meh-0.43.tar.gz
new file mode 100644
index 0000000..d19374d
Binary files /dev/null and b/pkgbuilds/python-meh/python-meh-0.43.tar.gz differ
diff --git a/pkgbuilds/python-meh/src/python-meh-0.43.tar.gz b/pkgbuilds/python-meh/src/python-meh-0.43.tar.gz
new file mode 120000
index 0000000..cc9b758
--- /dev/null
+++ b/pkgbuilds/python-meh/src/python-meh-0.43.tar.gz
@@ -0,0 +1 @@
+/home/nocode/Files/FWS-ISO/pkgbuilds/python-meh/python-meh-0.43.tar.gz
\ No newline at end of file
diff --git a/pkgbuilds/python-meh/src/python-meh-r0.43-1/.gitignore b/pkgbuilds/python-meh/src/python-meh-r0.43-1/.gitignore
new file mode 100644
index 0000000..60074ed
--- /dev/null
+++ b/pkgbuilds/python-meh/src/python-meh-r0.43-1/.gitignore
@@ -0,0 +1,14 @@
+*.pyc
+.*.swp
+
+python-meh*.tar.gz
+
+ChangeLog
+
+po/*.gmo
+po/*.po
+po/POTFILES
+
+build/
+
+.directory
diff --git a/pkgbuilds/python-meh/src/python-meh-r0.43-1/COPYING b/pkgbuilds/python-meh/src/python-meh-r0.43-1/COPYING
new file mode 100644
index 0000000..a43ea21
--- /dev/null
+++ b/pkgbuilds/python-meh/src/python-meh-r0.43-1/COPYING
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ Appendix: How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C) 19yy
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ , 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/pkgbuilds/python-meh/src/python-meh-r0.43-1/MANIFEST.in b/pkgbuilds/python-meh/src/python-meh-r0.43-1/MANIFEST.in
new file mode 100644
index 0000000..7d6499c
--- /dev/null
+++ b/pkgbuilds/python-meh/src/python-meh-r0.43-1/MANIFEST.in
@@ -0,0 +1,6 @@
+include COPYING Makefile ChangeLog
+recursive-include meh *.py
+recursive-include ui *.glade
+recursive-include pixmaps *.png
+recursive-include po *.po *.pot Makefile
+recursive-include tests *.py
diff --git a/pkgbuilds/python-meh/src/python-meh-r0.43-1/Makefile b/pkgbuilds/python-meh/src/python-meh-r0.43-1/Makefile
new file mode 100644
index 0000000..c736a31
--- /dev/null
+++ b/pkgbuilds/python-meh/src/python-meh-r0.43-1/Makefile
@@ -0,0 +1,101 @@
+PKGNAME=python-meh
+VERSION=$(shell awk '/Version:/ { print $$2 }' $(PKGNAME).spec)
+RELEASE=$(shell awk '/Release:/ { print $$2 }' $(PKGNAME).spec | sed -e 's|%.*$$||g')
+TAG=r$(VERSION)-$(RELEASE)
+
+PREFIX=/usr
+
+PYTHON2=python2
+PYTHON3=python3
+PYTHON=$(PYTHON2)
+
+TESTSUITE:=tests/baseclass.py
+
+PYCHECKEROPTS=--no-argsused --no-miximport --maxargs 0 --no-local -\# 0 --only -Q
+
+ZANATA_PULL_ARGS = --transdir po/
+ZANATA_PUSH_ARGS = --srcdir po/ --push-type source --force
+
+default: all
+
+all:
+ $(MAKE) -C po
+
+clean:
+ -rm *.tar.gz meh/*.pyc meh/ui/*.pyc tests/*.pyc ChangeLog
+ $(MAKE) -C po clean
+ $(PYTHON) setup.py -q clean --all
+
+test:
+ @echo "*** Running unittests with Python 2 ***"
+ PYTHONPATH=. $(PYTHON2) $(TESTSUITE) -v
+
+ @echo "*** Running unittests with Python 3 ***"
+ PYTHONPATH=. $(PYTHON3) $(TESTSUITE) -v
+
+check:
+ PYTHONPATH=. tests/pylint/runpylint.py
+
+install:
+ $(PYTHON) setup.py install --root=$(DESTDIR)
+ $(MAKE) -C po install
+
+ChangeLog:
+ (GIT_DIR=.git git log > .changelog.tmp && mv .changelog.tmp ChangeLog; rm -f .changelog.tmp) || (touch ChangeLog; echo 'git directory not found: installing possibly empty changelog.' >&2)
+
+tag:
+ git tag -a -m "Tag as $(TAG)" -f $(TAG)
+ @echo "Tagged as $(TAG)"
+
+release: tag archive
+
+archive: po-pull
+ @rm -f ChangeLog
+ @make ChangeLog
+ git archive --format=tar --prefix=$(PKGNAME)-$(VERSION)/ $(TAG) > $(PKGNAME)-$(VERSION).tar
+ mkdir $(PKGNAME)-$(VERSION)
+ cp -r po $(PKGNAME)-$(VERSION)
+ cp ChangeLog $(PKGNAME)-$(VERSION)/
+ tar -rf $(PKGNAME)-$(VERSION).tar $(PKGNAME)-$(VERSION)
+ gzip -9 $(PKGNAME)-$(VERSION).tar
+ rm -rf $(PKGNAME)-$(VERSION)
+ git checkout -- po/$(PKGNAME).pot
+ @echo "The archive is in $(PKGNAME)-$(VERSION).tar.gz"
+
+local: po-pull
+ @rm -f ChangeLog
+ @make ChangeLog
+ @rm -rf $(PKGNAME)-$(VERSION).tar.gz
+ @rm -rf /tmp/$(PKGNAME)-$(VERSION) /tmp/$(PKGNAME)
+ @dir=$$PWD; cp -a $$dir /tmp/$(PKGNAME)-$(VERSION)
+ @cd /tmp/$(PKGNAME)-$(VERSION) ; $(PYTHON) setup.py -q sdist
+ @cp /tmp/$(PKGNAME)-$(VERSION)/dist/$(PKGNAME)-$(VERSION).tar.gz .
+ @rm -rf /tmp/$(PKGNAME)-$(VERSION)
+ @echo "The archive is in $(PKGNAME)-$(VERSION).tar.gz"
+
+rpmlog:
+ @git log --pretty="format:- %s (%ae)" $(TAG).. |sed -e 's/@.*)/)/'
+ @echo
+
+potfile:
+ $(MAKE) -C po potfile
+
+po-pull:
+ rpm -q zanata-python-client &>/dev/null || ( echo "need to run: dnf install zanata-python-client"; exit 1 )
+ zanata pull $(ZANATA_PULL_ARGS)
+
+bumpver: potfile
+ zanata push $(ZANATA_PUSH_ARGS) || ( echo "zanata push failed"; exit 1 )
+ @NEWSUBVER=$$((`echo $(VERSION) |cut -d . -f 2` + 1)) ; \
+ NEWVERSION=`echo $(VERSION).$$NEWSUBVER |cut -d . -f 1,3` ; \
+ DATELINE="* `LANG=c date "+%a %b %d %Y"` `git config user.name` <`git config user.email`> - $$NEWVERSION-1" ; \
+ cl=`grep -n %changelog python-meh.spec |cut -d : -f 1` ; \
+ tail --lines=+$$(($$cl + 1)) python-meh.spec > speclog ; \
+ (head -n $$cl python-meh.spec ; echo "$$DATELINE" ; make --quiet rpmlog 2>/dev/null ; echo ""; cat speclog) > python-meh.spec.new ; \
+ mv python-meh.spec.new python-meh.spec ; rm -f speclog ; \
+ sed -i "s/Version: $(VERSION)/Version: $$NEWVERSION/" python-meh.spec ; \
+ sed -i "s/version='$(VERSION)'/version='$$NEWVERSION'/" setup.py
+
+ci: check test
+
+.PHONY: clean install tag archive local
diff --git a/pkgbuilds/python-meh/src/python-meh-r0.43-1/examples/meh_test.py b/pkgbuilds/python-meh/src/python-meh-r0.43-1/examples/meh_test.py
new file mode 100755
index 0000000..5725b7d
--- /dev/null
+++ b/pkgbuilds/python-meh/src/python-meh-r0.43-1/examples/meh_test.py
@@ -0,0 +1,38 @@
+#!/usr/bin/python -tbBQnew
+# -*- coding: utf-8 -*-
+
+import sys
+import meh
+import meh.handler
+import meh.dump
+import subprocess
+
+class MyTestError(Exception):
+ pass
+
+class MyObject(object):
+ def __init__(self):
+ self.unicode_str = u"řčšěčšěčěš"
+ self.encoded_unicode_str = self.unicode_str.encode("utf-8")
+ self.binary_data = '\xff\xff\xfe'
+
+if __name__ == "__main__":
+ print("***Running python-meh test***")
+
+ config = meh.Config(programName="myMehTest", programVersion="1.0-1",
+ programArch="noarch",
+ fileList=["/proc/cmdline", "unicode.txt", "ascii.txt"])
+
+ if len(sys.argv) > 1 and sys.argv[1] == "g":
+ import meh.ui.gui
+ intf = meh.ui.gui.GraphicalIntf()
+ else:
+ import meh.ui.text
+ intf = meh.ui.text.TextIntf()
+
+ handler = meh.handler.ExceptionHandler(config, intf,
+ meh.dump.ExceptionDump)
+ handler.install(MyObject())
+
+ #raise MyTestError("My testing exception.")
+ subprocess.call(["non_existing_executable"])
diff --git a/pkgbuilds/python-meh/src/python-meh-r0.43-1/meh/__init__.py b/pkgbuilds/python-meh/src/python-meh-r0.43-1/meh/__init__.py
new file mode 100644
index 0000000..6d554af
--- /dev/null
+++ b/pkgbuilds/python-meh/src/python-meh-r0.43-1/meh/__init__.py
@@ -0,0 +1,139 @@
+#
+# __init__.py - Basic stuff for exception handling
+#
+# Copyright (C) 2009 Red Hat, Inc.
+# All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#
+# Author(s): Chris Lumens
+#
+
+from collections import namedtuple
+
+# These constants represent the return values of buttons on the initial
+# exception handling dialog - the dialog that first pops up when an
+# exception is hit.
+MAIN_RESPONSE_DEBUG = 0
+MAIN_RESPONSE_SAVE = 1
+MAIN_RESPONSE_QUIT = 2
+MAIN_RESPONSE_SHELL = 3
+MAIN_RESPONSE_NONE = 4
+
+class ConfigError(Exception):
+ """Exception class for the configuration related errors."""
+
+ pass
+
+class Config(object):
+ """Hold configuration info useful throughout the exception handling
+ classes. This prevents having to pass a bunch of arguments to a
+ bunch of different functions. A Config instance must be created
+ before creating an ExceptionHandler instance.
+ """
+ def __init__(self, *args, **kwargs):
+ """Create a new Config instance. Arguments may be passed in to
+ set any of the configuration values. Unknown arguments will
+ be ignored. Instance attributes:
+
+ attrSkipList -- A list of strings. When handling a traceback,
+ any attributes found with the same name as an
+ element of this list will not be written to
+ the dump. This is to prevent writing potentially
+ sensitive information like passwords. The names
+ must be given without the leading name of the
+ object passed to handler.install(). For instance,
+ if handler.install() gets an Anaconda instance
+ with the name "anaconda" and you want to skip
+ anaconda.id.rootPassword, "id.rootPassword" should
+ be listed in attrSkipList.
+ fileList -- A list of files to find on the system and add
+ to the traceback dump.
+ localSkipList -- A list of strings. When handling a traceback,
+ any local variables found with their name containing
+ an element of this list will not be written to
+ the dump. This is subtely different from
+ attrSkipList.
+ callbackDict -- A dictionary having item names as keys and pairs
+ (function, attchmnt_only) as values.
+ @see register_callback
+ programName -- The name of the erroring program.
+ programVersion -- The version number of the erroring program.
+ Both programName and programVersion are used
+ throughout the exception handler, so must be
+ set.
+ programArch -- The architecture of the erroring program.
+ """
+ self.attrSkipList = []
+ self.fileList = []
+ self.localSkipList = []
+ self.callbackDict = dict()
+ self.programName = None
+ self.programVersion = None
+ self.programArch = None
+
+ # Override the defaults set above with whatever's passed in as an
+ # argument. Unknown arguments get thrown away.
+ for (key, val) in kwargs.items():
+ if key in self.__dict__:
+ self.__dict__[key] = val
+
+ # Make sure required things are set.
+ if not self.programName:
+ raise ValueError("programName must be set.")
+
+ if not self.programVersion:
+ raise ValueError("programVersion must be set.")
+
+ def register_callback(self, item_name, callback, attchmnt_only=False,
+ override=False):
+ """
+ Register new callback that will be called when data about the
+ crash is collected. The returned value will be included as the
+ 'item_name' item.
+
+ :param item_name: name of the item storing the value returned by the
+ 'callback' function
+ :type item_name: string
+ :param callback: a function to be called
+ :type callback: a function of type: () -> string
+ :param attchmnt_only: whether the returned valued should be included only
+ as an attachment or also in the main '*-tb' file
+ (defaults to False)
+ :type attchmnt_only: bool
+ :param override: whether to override the previously registered callback
+ with the same item name or not (may raise exception)
+ :type override: bool
+
+ :raise ConfigError: if callback with the 'item_name' has already been
+ registered and 'override' is not set to True
+ :return: None
+
+ """
+
+ if item_name not in self.callbackDict or override:
+ self.callbackDict[item_name] = (callback, attchmnt_only)
+ else:
+ msg = "Callback with name '%s' already registered" % item_name
+ raise ConfigError(msg)
+
+# type, value and stack are items provided by Python for the exception handler
+ExceptionInfo = namedtuple("ExceptionInfo", ["type", "value", "stack"])
+
+# ExceptionInfo instance plus the object that should be dumped
+DumpInfo = namedtuple("DumpInfo", ["exc_info", "object"])
+
+# information about a package (as provided e.g. by rpm)
+PackageInfo = namedtuple("PackageInfo", ["name", "version", "release", "epoch",
+ "arch"])
diff --git a/pkgbuilds/python-meh/src/python-meh-r0.43-1/meh/dump.py b/pkgbuilds/python-meh/src/python-meh-r0.43-1/meh/dump.py
new file mode 100644
index 0000000..c746e57
--- /dev/null
+++ b/pkgbuilds/python-meh/src/python-meh-r0.43-1/meh/dump.py
@@ -0,0 +1,551 @@
+#
+# dump.py - general exception formatting and saving
+#
+# Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2009 Red Hat, Inc.
+# All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#
+# Author(s): Matt Wilson
+# Erik Troan
+# Chris Lumens
+#
+# pylint: disable=bare-except,broad-except
+
+import copy
+import inspect
+import os
+import traceback
+import sys
+import codecs
+import six
+from meh import PackageInfo
+from meh.safe_string import SafeStr
+
+# Python 2/3 compatibilty
+try:
+ longtype = long # pylint: disable=undefined-variable
+except NameError:
+ longtype = int
+
+try:
+ unitype = unicode # pylint: disable=undefined-variable
+except NameError:
+ unitype = str
+
+class ExceptionDump(object):
+ """This class represents a traceback and contains several useful methods
+ for manipulating a traceback. In general, clients should not have to
+ use this class. It is mainly for internal use.
+ """
+ def __init__(self, exc_info, config_obj):
+ """Create a new ExceptionDump instance. Instance attributes:
+
+ :param exc_info: info about the exception provided by Python
+ :type exc_info: an instance of the meh.ExceptionInfo class
+ :param config_obj: configuration for python-meh
+ :type config_obj: an instance of the meh.Config class
+ """
+
+ if inspect.istraceback(exc_info.stack):
+ self.stack = inspect.getinnerframes(exc_info.stack)
+ else:
+ self.stack = exc_info.stack
+
+ self.conf = config_obj
+ self.type = exc_info.type
+ self.value = exc_info.value
+
+ self._dumpHash = {}
+
+ @property
+ def desc(self):
+ # The description is a single line of text that should be used anywhere
+ # the bug needs to quickly be summarized. The most obvious example of
+ # this is when saving to bugzilla. We can populate the UI with this
+ # string so the user doesn't need to come up with one.
+ if self.type and self.value:
+ return traceback.format_exception_only(self.type, self.value)[0].strip()
+ else:
+ return ""
+
+ def _get_environment_info(self):
+ """
+ Returns dictionary containing these items for a bugreport header
+
+ architecture
+ cmdline
+ component
+ executable
+ kernel
+ package
+ release
+ other involved packages
+
+ :rtype: dict (string -> string)
+ """
+
+ RELEASE_NAME_FILE = "/etc/system-release"
+
+ class RPMinfoError(Exception):
+ """Exception for errors in rpm queries"""
+ pass
+
+ def get_python_opts():
+ """
+ Returns python's command line options
+
+ :return: list of python's command line options
+ :rtype: list
+
+ """
+
+ flags_to_opts = {
+ "debug": "-d",
+ "py3k_warning": "-3",
+ "division_new": "-Qnew",
+ "division_warning": "-Q",
+ "inspect": "-i",
+ "interactive": "-i",
+ "optimize": "-O",
+ "dont_write_bytecode": "-B",
+ "no_user_site": "-s",
+ "no_site": "-S",
+ "ignore_environment": "-E",
+ "tabcheck": "-t",
+ "verbose": "-v",
+ "unicode": "-u",
+ "bytes_warning": "-b",
+ "quiet": "-q",
+ }
+
+ ret = set()
+ for flag in flags_to_opts.keys():
+ if getattr(sys.flags, flag, None):
+ ret.add(flags_to_opts[flag])
+ ret = list(ret)
+ return ret
+
+ def get_package_and_component(file_=sys.argv[0]):
+ """
+ Returns package and component names for file (by default for script itself).
+
+ :param file_: filename
+ :type file_: string
+ :return: tuple containing package info and component name for the file
+ :rtype: (PackageInfo, str)
+ :raise RPMinfoError: if package and component for the file cannot be found
+
+ """
+
+ import rpm
+ ts = rpm.TransactionSet()
+ mi = ts.dbMatch("basenames", file_)
+ try:
+ header = next(mi)
+ except StopIteration:
+ raise RPMinfoError("Cannot get package and component for file "+
+ "{0}".format(file_))
+ pkg_info = PackageInfo(header["name"].decode("utf-8"), header["version"].decode("utf-8"),
+ header["release"].decode("utf-8"),
+ u"%d" % header["epoch"] if header["epoch"] else u"0",
+ header["arch"].decode("utf-8"))
+
+ # cuts the name from the NVR format: foo-blah-2.8.8-2.fc17.src.rpm
+ srpm_name = header["sourcerpm"].decode("utf-8")
+ name_end = len(srpm_name)
+ try:
+ name_end = srpm_name.rindex('-')
+ name_end = srpm_name[:name_end].rindex('-')
+ except ValueError:
+ # expected exception
+ pass
+
+ component = srpm_name[:name_end]
+
+ return (pkg_info, component)
+
+ def get_release_version():
+ """Returns release version (according to RELEASE_NAME_FILE)"""
+ try:
+ with open(RELEASE_NAME_FILE, "r") as file_:
+ return file_.readline().rstrip()
+ except IOError:
+ return "Cannot get release name."
+
+ def get_other_packages(self):
+ """
+ Returns a set of additional packages whose files appear in
+ traceback.
+
+ :rtype: set
+
+ """
+ packages = set()
+ if not self.stack:
+ return packages
+
+ for (_frame, fn, _lineno, _func, _ctx, _idx) in self.stack:
+ try:
+ pkg_info = get_package_and_component(fn)[0]
+ package = "{0.name}-{0.version}-{0.release}.{0.arch}".format(
+ pkg_info)
+ packages.add(package)
+ except RPMinfoError:
+ continue
+
+ try:
+ pkg_info = get_package_and_component()[0]
+ package = "{0.name}-{0.version}-{0.release}.{0.arch}".format(
+ pkg_info)
+ packages.discard(package)
+ except RPMinfoError:
+ pass
+ return packages
+
+ def get_environment_variables():
+ """
+ Returns a list of strings containing defined environment
+ variables and their values in the following format:
+
+ VARIABLE=VALUE
+
+ :rtype: list
+
+ """
+
+ ret = list()
+ for (key, value) in os.environ.items():
+ ret.append("{0}={1}".format(key, value))
+
+ return ret
+
+
+ #--begining of the method _get_environment_info--
+ try:
+ pkg_info, component = get_package_and_component()
+ except RPMinfoError:
+ pkg_info = None
+ component = None
+
+ other_packages = ", ".join(get_other_packages(self))
+
+ ret = dict()
+ ret["architecture"] = os.uname()[4]
+ ret["cmdline"] = "{0} {1} {2}".format(sys.executable,
+ " ".join(get_python_opts()),
+ " ".join(sys.argv))
+ if component:
+ ret["component"] = component
+ ret["executable"] = sys.argv[0]
+ ret["kernel"] = os.uname()[2]
+ if pkg_info:
+ ret["pkg_info"] = pkg_info
+ ret["release"] = get_release_version()
+ if other_packages:
+ ret["other involved packages"] = other_packages
+ ret["environ"] = "\n".join(get_environment_variables())
+
+ return ret
+
+ @property
+ def environment_info(self):
+ return self._get_environment_info()
+
+ def __str__(self):
+ lst = self._format_stack()
+
+ lst.insert(0, "%s %s exception report\n" % (self.conf.programName, self.conf.programVersion))
+ lst.insert(1, "Traceback (most recent call last):\n")
+
+ if self.type is not None and self.value is not None:
+ lst.extend(traceback.format_exception_only(self.type, self.value))
+
+ return "".join(lst)
+
+ def _format_stack(self):
+ if not self.stack:
+ return []
+
+ frames = []
+ for (_frame, fn, lineno, func, ctx, _idx) in self.stack:
+ if isinstance(ctx, list):
+ code = "".join(ctx)
+ else:
+ code = ctx
+
+ frames.append((fn, lineno, func, code))
+
+ return traceback.format_list(frames)
+
+ # Create a string representation of a class. This method will recursively
+ # handle all attributes of the base given class.
+ def _dumpClass(self, instance, level=0, parentkey="", skipList=None):
+ # This is horribly cheesy, and bound to require maintainence in the
+ # future. The problem is that anything subclassed from object fails
+ # the types.InstanceType test we used to have which means those
+ # objects never get dumped. However, most everything basic in
+ # python is derived from object so we can't just check for the super
+ # class. Any instances of the following types will just be printed
+ # out, and everything else will be assumed to be something that
+ # needs to be recursed on.
+ def __isSimpleType(instance):
+ return isinstance(instance, bool) or \
+ isinstance(instance, complex) or \
+ isinstance(instance, float) or \
+ isinstance(instance, int) or \
+ isinstance(instance, longtype) or \
+ isinstance(instance, type(None)) or \
+ isinstance(instance, bytes) or \
+ isinstance(instance, str) or \
+ isinstance(instance, unitype) or \
+ not hasattr(instance, "__class__") or \
+ not hasattr(instance, "__dict__")
+
+ ret = SafeStr()
+
+ if not skipList:
+ skipList = []
+
+ # protect from loops
+ try:
+ # Store the id(), not the instance name to protect against
+ # instances that cannot be hashed.
+ if not id(instance) in self._dumpHash:
+ self._dumpHash[id(instance)] = None
+ else:
+ ret += "Already dumped (%s instance)\n" % instance.__class__.__name__
+ return ret
+ except TypeError:
+ ret += "Cannot dump object\n"
+ return ret
+
+ if ("__str__" in instance.__class__.__dict__ or
+ "__repr__" in instance.__class__.__dict__):
+ try:
+ ret += "%s\n" % (instance,)
+ except:
+ ret += "\n"
+
+ return ret
+
+ ret += "%s instance, containing members:\n" %\
+ (instance.__class__.__name__)
+ pad = ' ' * ((level) * 2)
+
+ for key, value in instance.__dict__.items():
+ if key.startswith("_%s__" % instance.__class__.__name__):
+ continue
+
+ if parentkey != "":
+ curkey = parentkey + "." + key
+ else:
+ curkey = key
+
+ # Don't dump objects that are in our skip list, though ones that are
+ # None are probably okay.
+ if eval("instance.%s is not None" % key) and \
+ eval("id(instance.%s)" % key) in skipList:
+ ret += "%s%s: Skipped\n" % (pad, curkey)
+ continue
+
+ if isinstance(value, list):
+ ret += "%s%s: [" % (pad, curkey)
+ first = 1
+ for item in value:
+ if not first:
+ ret += ", "
+ else:
+ first = 0
+
+ if __isSimpleType(item):
+ ret += "%s" % (item,)
+ else:
+ ret += self._dumpClass(item, level + 1, skipList=skipList)
+ ret += "]\n"
+ elif isinstance(value, dict):
+ # append things one after another so that e.g. binary data is
+ # replaced by hexa values separately
+ ret += pad
+ ret += curkey
+ ret += ": {"
+ first = 1
+ for k, v in value.items():
+ if not first:
+ ret += ", "
+ else:
+ first = 0
+ if isinstance(k, str) or isinstance(k, unitype):
+ ret += "'"
+ ret += k
+ ret += "': "
+ else:
+ ret += "%s: " % (k,)
+
+ if __isSimpleType(v):
+ ret += "%s" % (v,)
+ else:
+ ret += self._dumpClass(v, level + 1, parentkey = curkey, skipList=skipList)
+ ret += "}\n"
+ elif __isSimpleType(value):
+ ret += "%s%s: " % (pad, curkey)
+ ret += value
+ ret += "\n"
+ else:
+ ret += "%s%s: " % (pad, curkey)
+ ret += self._dumpClass(value, level + 1, parentkey=curkey, skipList=skipList)
+
+ return ret
+
+ def dump(self, obj):
+ """Dump the local variables and all attributes of a given object.
+ The lists of files and attrs to ignore are all taken from a
+ Config object instance provided when the ExceptionDump class was
+ created.
+
+ obj -- Any Python object. This object will have all its attributes
+ written out, except for those mentioned in the attrSkipList.
+ """
+ idSkipList = []
+ ret = SafeStr()
+
+ # We need to augment the environment eval() will run in with the
+ # bindings that were local when the traceback happened so that the
+ # idSkipList can be populated. However since we're not allowed to
+ # modify the results of locals(), we'll have to make a copy first.
+ localVars = copy.copy(locals())
+ if self.stack:
+ localVars.update(self.stack[-1][0].f_locals)
+
+ # Catch attributes that do not exist at the time we do the exception dump
+ # and ignore them.
+ for k in self.conf.attrSkipList:
+ try:
+ eval("idSkipList.append(id(obj.%s))" % k, None, localVars)
+ except:
+ pass
+
+ # Write local variables to the given file descriptor, ignoring any of
+ # the local skips.
+ if self.stack:
+ frame = self.stack[-1][0]
+ ret += "\nLocal variables in innermost frame:\n"
+ try:
+ for (key, value) in frame.f_locals.items():
+ lowered_key = key.lower()
+ if any(item.lower() in lowered_key
+ for item in self.conf.localSkipList):
+ continue
+
+ ret += "%s: %s\n" % (key, value)
+ except:
+ pass
+
+ # And now dump the object's attributes.
+ try:
+ ret += "\n\n"
+ ret += self._dumpClass(obj, skipList=idSkipList)
+ except:
+ ret += "\nException occurred during state dump:\n"
+ ret += traceback.format_exc(None)
+
+ # Filter out item names and callbacks that should appear
+ # only as attachments
+ items_callbacks = ((name, cb) for (name, (cb, attchmnt_only))
+ in self.conf.callbackDict.items()
+ if not attchmnt_only)
+
+ # And now add data returned by the registered callbacks
+ ret += "\nRegistered callbacks:\n"
+ for (item_name, callback) in items_callbacks:
+ try:
+ callback_ret = callback()
+ if callback_ret:
+ ret += "%s:\n%s\n" % (item_name, callback_ret)
+ except Exception as exc:
+ ret += "%s: Caused error: %s\n" % (item_name, exc)
+
+ # And finally, write a bunch of files into the dump too.
+ for fname in self.conf.fileList:
+ try:
+ if six.PY2:
+ with codecs.open(fname, "r", "utf-8", "ignore") as fobj:
+ ret += "\n\n%s:\n" % (fname,)
+ for line in fobj:
+ ret += line.encode("utf-8")
+ else:
+ with open(fname, "rt", encoding="utf-8") as fobj:
+ ret += "\n\n%s:\n" % (fname,)
+ for line in fobj:
+ ret += line
+ except IOError:
+ pass
+ except:
+ ret += "\nException occurred during %s file copy:\n" % (fname,)
+ ret += traceback.format_exc(None)
+
+ return ret
+
+ @property
+ def hash(self):
+ """Create a hash for this traceback object. This is most suitable for
+ searching bug filing systems for duplicates. The hash is composed
+ of the basename of each file in the stack, the method names, and
+ the bit of code. Line numbers and the actual exception message
+ itself are left out.
+ """
+ import hashlib
+ s = ""
+
+ if self.stack:
+ for (file, _lineno, func, text) in [f[1:5] for f in self.stack]:
+ if isinstance(text, list):
+ text = "".join(text)
+ s += "%s %s %s\n" % (os.path.basename(file), func, text)
+ else:
+ s = "%s %s" % (self.type, self.value)
+
+ return hashlib.sha256(s.encode("utf-8")).hexdigest()
+
+ def traceback_and_object_dump(self, obj):
+ """
+ Return the traceback and a text representation of an object.
+
+ :param obj: Any Python object. This object will have all its attributes
+ written out, except for those mentioned in the attrSkipList.
+
+ """
+
+ ret = SafeStr(str(self))
+ ret += self.dump(obj)
+
+ return ret
+
+class ReverseExceptionDump(ExceptionDump):
+ """This class provides an alternate representation of an exception. In
+ this representation, the traceback is printed with the most recent call
+ at the top of the stack, and the most generic call at the bottom. Note
+ that this order does not affect the hash at all.
+ """
+ def __str__(self):
+ lst = self._format_stack()
+ lst.reverse()
+
+ lst.insert(0, "%s %s exception report\n" % (self.conf.programName, self.conf.programVersion))
+ lst.insert(1, "Traceback (most recent call first):\n")
+
+ if self.type is not None and self.value is not None:
+ lst.extend(traceback.format_exception_only(self.type, self.value))
+
+ return "".join(lst)
diff --git a/pkgbuilds/python-meh/src/python-meh-r0.43-1/meh/handler.py b/pkgbuilds/python-meh/src/python-meh-r0.43-1/meh/handler.py
new file mode 100644
index 0000000..6ae0a5d
--- /dev/null
+++ b/pkgbuilds/python-meh/src/python-meh-r0.43-1/meh/handler.py
@@ -0,0 +1,299 @@
+# Copyright (C) 2009 Red Hat, Inc.
+# All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#
+# Author(s): Chris Lumens
+#
+from meh import MAIN_RESPONSE_DEBUG, MAIN_RESPONSE_SAVE, MAIN_RESPONSE_SHELL, MAIN_RESPONSE_QUIT, DumpInfo, ExceptionInfo
+import bdb
+import os
+import sys
+import report
+import traceback
+import subprocess
+
+import gettext
+_ = lambda x: gettext.ldgettext("python-meh", x)
+
+class NoNetwork(Exception):
+ def __init__(self, msg=""):
+ Exception.__init__(self)
+ self.msg = msg
+
+ def __str__(self):
+ return "No network is available"
+
+class ExceptionHandler(object):
+ def __init__(self, confObj, intfClass, exnClass):
+ """Create a new ExceptionHandler instance. Subclasses of this one may
+ be created, provided they are careful to provide all the methods
+ this one does. Instance attributes:
+
+ conf -- A filled in Config instance. At least the programName
+ and programVersion are required.
+ exnClass -- An instance of ExceptionDump or a subclass of it. This
+ is required to know how to represent the Python
+ exception internally.
+ intfClass-- An instance of AbstractIntf. This is required to know
+ what UI classes to use.
+ """
+ self.conf = confObj
+ self.exnClass = exnClass
+ self.intf = intfClass
+
+ self._exitcode = 10
+ self._exn = None
+ self.exnFile = None
+ self.exnText = ""
+
+ def _setExitCode(self, code):
+ self._exitcode = code
+
+ exitcode = property(lambda s: s._exitcode,
+ lambda s, v: s._setExitCode(v))
+
+ def _setExn(self, e):
+ self._exn = e
+
+ exn = property(lambda s: s._exn,
+ lambda s, v: s._setExn(v))
+
+ def handleException(self, dump_info):
+ """This is the main exception handling entry point. When Python
+ gets an exception it doesn't know how to handle, this method will
+ be called. It then saves the traceback and displays the main
+ dialog asking the user what to do next. Once this method is
+ called, there's no good way to go back to what you were doing
+ before.
+
+ All arguments are passed in from the handler created by calling
+ self.install(). This method should not usually be overridden by
+ a subclass.
+
+ :type dump_info: an instance of the meh.DumpInfo class
+
+ """
+
+ responseHash = { MAIN_RESPONSE_QUIT: self.runQuit,
+ MAIN_RESPONSE_DEBUG: self.runDebug,
+ MAIN_RESPONSE_SAVE: self.runSave,
+ MAIN_RESPONSE_SHELL: self.runShell,
+ }
+
+ # Quit if we got an exception when running pdb.
+ if isinstance(dump_info.exc_info.value, bdb.BdbQuit):
+ sys.exit(self.exitcode)
+
+ # Restore original exception handler.
+ sys.excepthook = sys.__excepthook__
+
+ self.preWriteHook(dump_info)
+
+ # Save the exception to the filesystem first.
+ self.exn = self.exnClass(dump_info.exc_info, self.conf)
+ (fobj, self.exnFile) = self.openFile()
+ self.exnText = self.exn.traceback_and_object_dump(dump_info.object)
+ fobj.write(self.exnText)
+ fobj.close()
+
+ self.postWriteHook(dump_info)
+
+ # Run initial UI screen, asking whether to save/debug/quit.
+ while True:
+ win = self.intf.mainExceptionWindow(str(self.exn), self.exnText)
+ if not win:
+ self.runQuit(dump_info.exc_info)
+
+ rc = win.run()
+ win.destroy()
+
+ try:
+ responseHash[rc](dump_info.exc_info)
+ except KeyError:
+ # This happens if (for example) the user hits Escape instead
+ # of pressing a button.
+ continue
+
+ win.destroy()
+
+ def preWriteHook(self, dump_info):
+ """Subclasses may supply a function with this name that will be
+ called immediately before the traceback is written to disk in
+ order to have any sort of special pre-write processing that needs
+ to be done.
+
+ :type dump_info: an instance of the meh.DumpInfo class
+ """
+ pass
+
+ def postWriteHook(self, dump_info):
+ """Subclasses may supply a function with this name that will be
+ called immediately after the traceback is written to disk, but
+ immediately before the UI is run. This is to provide a place for
+ any special handling to happen once there is a file on disk.
+
+ :type dump_info: an instance of the meh.DumpInfo class
+ """
+ pass
+
+ def install(self, obj):
+ """Install ourselves as the top level exception handler with Python.
+ If this method is not called after an ExceptionHandler instance is
+ created, none of the rest of this code will ever be called.
+
+ obj -- A Python object that may be dumped to a file when the
+ exception is saved. This should be something like the top
+ level object in a program.
+ """
+ sys.excepthook = lambda ty, value, tb: \
+ self.handleException(DumpInfo(ExceptionInfo(ty, value, tb), obj))
+
+ def openFile(self):
+ """Create a randomly named output file to write the exception dump to.
+ This requires a programName be set in the Config instance. The
+ return value is a (file descriptor, path) pair. The file must be
+ closed by the caller when writing is done. Subclasses should not
+ override this method unless they know what they're doing.
+ """
+ import tempfile
+ (fd, path) = tempfile.mkstemp("", "%s-tb-" % self.conf.programName, "/tmp")
+ fo = os.fdopen(fd, "w")
+ return (fo, path)
+
+ ### Methods called by the primary exception handling dialog.
+
+ def runQuit(self, exc_info):
+ """This method is called when the "Exit" button is clicked. It may
+ be overridden by a subclass, but the basic behavior of eventually
+ quitting should be preserved.
+
+ :type exc_info: an instance of the meh.ExceptionInfo class
+ """
+ sys.exit(self.exitcode)
+
+ def runDebug(self, exc_info):
+ """This method is called when the "Debug" button is clicked. It may
+ be overridden by a subclass if specialized behavior is required to
+ enter debug mode.
+
+ :type exc_info: an instance of the meh.ExceptionInfo class
+ """
+
+ print("")
+ print(_("Use 'continue' command to quit the debugger and get back to "
+ "the main menu"))
+ import pdb
+ pdb.post_mortem(exc_info.stack)
+ #no need to quit here, let's just get back to the main dialog
+
+ def runShell(self, exc_info):
+ """This method is called when user requests to run a shell. It may
+ be overridden by a subclass if specialized behavior is required to
+ run shell (e.g. TTY switching).
+
+ :type exc_info: an instance of the meh.ExceptionInfo class
+
+ """
+
+ print("")
+ print(_("Exit the shell to get back to the main menu"))
+ proc = subprocess.Popen(["bash", "--login"], shell=True, cwd="/")
+ proc.wait()
+
+ def runSave(self, exc_info):
+ """This method is called when the "Save" button is clicked. It may
+ be overridden by a subclass, but that's going to be a lot of work.
+
+ :type exc_info: an instance of the meh.ExceptionInfo class
+ """
+
+ params = dict()
+ params.update(self.exn.environment_info)
+ pkg_info = params.pop("pkg_info", None)
+
+ #if rpmdb queries fail (anaconda), use self.conf.* values
+ if "component" not in params:
+ params["component"] = self.conf.programName
+
+ if pkg_info:
+ params["pkg_name"] = pkg_info.name
+ params["pkg_version"] = pkg_info.version
+ params["pkg_release"] = pkg_info.release
+ params["pkg_epoch"] = pkg_info.epoch
+ params["pkg_arch"] = pkg_info.arch
+ params["package"] = "%s-%s-%s.%s" % (pkg_info.name, pkg_info.version,
+ pkg_info.release, pkg_info.arch)
+ else:
+ params["pkg_name"] = self.conf.programName
+ parts = self.conf.programVersion.rsplit("-", 1)
+ if len(parts) == 2:
+ version, release = parts
+ else:
+ version = parts[0]
+ # not given, make something up
+ release = "1"
+ params["pkg_version"] = version
+ params["pkg_release"] = release
+ # we don't support specifying epoch in the config object and only
+ # the anaconda which is not using epochs doesn't have the RPMdb to
+ # query
+ params["pkg_epoch"] = "0"
+ params["pkg_arch"] = self.conf.programArch
+ params["package"] = "{0.programName}-{0.programVersion}".format(
+ self.conf)
+
+ params["hashmarkername"] = self.conf.programName
+ params["duphash"] = self.exn.hash
+ params["reason"] = self.exn.desc
+ params["description"] = "The following was filed automatically by %s:\n%s" \
+ % (self.conf.programName, str(self.exn))
+
+ tb_item_name = "%s-tb" % self.conf.programName
+ params[tb_item_name] = self.exnText
+
+ # save data generated by callbacks
+ for (item_name, (callback, _anc)) in self.conf.callbackDict.items():
+ try:
+ ret = callback()
+ if ret:
+ params[item_name] = ret
+ except Exception as exc: # pylint: disable=broad-except
+ params[item_name] = "Caused error: %s" % exc
+
+ for fpath in self.conf.fileList:
+ try:
+ with open(fpath, "r") as fobj:
+ # would be better to use the full path here, but libreport
+ # doesn't allow '/' characters in the item name
+ filename = os.path.basename(fpath)
+ if filename not in params:
+ params[filename] = fobj.read()
+ else:
+ params[filename+"_file"] = fobj.read()
+ except: # pylint: disable=bare-except
+ #skip files we cannot read
+ continue
+
+ # tell ABRT some more things it requires
+ params["analyzer"] = "Python"
+ params["backtrace"] = "".join(traceback.format_exception(exc_info.type,
+ exc_info.value,
+ exc_info.stack))
+
+ signature = report.createPythonUnhandledExceptionSignature(**params)
+
+ # We don't want to automatically quit here since the user may wish to
+ # save somewhere else, debug, etc.
+ self.intf.saveExceptionWindow(signature)
diff --git a/pkgbuilds/python-meh/src/python-meh-r0.43-1/meh/network.py b/pkgbuilds/python-meh/src/python-meh-r0.43-1/meh/network.py
new file mode 100644
index 0000000..ede2b8a
--- /dev/null
+++ b/pkgbuilds/python-meh/src/python-meh-r0.43-1/meh/network.py
@@ -0,0 +1,45 @@
+#
+# network.py - network configuration install data
+#
+# Copyright (C) 2008, 2009 Red Hat, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#
+# Author(s): David Cantrell
+#
+import dbus
+
+# This code was taken from anaconda, and slightly adapted to not rely
+# on isys.
+NM_SERVICE = "org.freedesktop.NetworkManager"
+NM_MANAGER_PATH = "/org/freedesktop/NetworkManager"
+
+NM_STATE_CONNECTED = 3
+
+DBUS_PROPS_IFACE = "org.freedesktop.DBus.Properties"
+
+def hasActiveNetDev():
+ """Does the system have an enabled network interface?"""
+ try:
+ bus = dbus.SystemBus()
+ nm = bus.get_object(NM_SERVICE, NM_MANAGER_PATH)
+ props = dbus.Interface(nm, DBUS_PROPS_IFACE)
+ state = props.Get(NM_SERVICE, "State")
+
+ if int(state) == NM_STATE_CONNECTED:
+ return True
+ else:
+ return False
+ except dbus.DBusException:
+ return False
diff --git a/pkgbuilds/python-meh/src/python-meh-r0.43-1/meh/safe_string.py b/pkgbuilds/python-meh/src/python-meh-r0.43-1/meh/safe_string.py
new file mode 100644
index 0000000..714ce37
--- /dev/null
+++ b/pkgbuilds/python-meh/src/python-meh-r0.43-1/meh/safe_string.py
@@ -0,0 +1,71 @@
+#
+# Copyright (C) 2013 Red Hat, Inc.
+# All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#
+# Author: Vratislav Podzimek
+#
+#
+
+import sys
+PY = int(sys.version.split('.')[0])
+
+"""
+This module provides a SafeStr class.
+
+@see: SafeStr
+
+"""
+
+class SafeStr(str):
+ """
+ String class that has a modified __add__ method so that ascii strings,
+ binary data represented as a byte string and unicode objects can be
+ safely appended to it (not causing traceback). BINARY DATA IS OMITTED.
+
+ """
+
+ def __add__(self, other):
+
+ if PY > 2:
+ return SafeStr(str.__add__(self, str(other)))
+
+ if not (isinstance(other, str) or isinstance(other, unicode)): # pylint: disable=undefined-variable
+ if hasattr(other, "__str__"):
+ other = other.__str__()
+ else:
+ other = "OMITTED OBJECT WITHOUT __str__ METHOD"
+
+ if isinstance(other, unicode): # pylint: disable=undefined-variable
+ ret = SafeStr(str.__add__(self, other.encode("utf-8")))
+ else:
+ try:
+ # try to decode which doesn't cause traceback for utf-8 encoded
+ # non-ascii string and ascii string
+ other.decode("utf-8")
+ ret = SafeStr(str.__add__(self, other))
+ except UnicodeDecodeError:
+ # binary data, get the representation used by Python for
+ # non-ascii bytes
+
+ # hex(255) returns "0xff", we want "\xff"
+ other_hexa = (hex(ord(char)) for char in other)
+ other_backslashed = (hex_num.replace("0x", "\\x")
+ for hex_num in other_hexa)
+ other_repr = "".join(other_backslashed)
+
+ ret = SafeStr(str.__add__(self, other_repr))
+
+ return ret
diff --git a/pkgbuilds/python-meh/src/python-meh-r0.43-1/meh/ui/__init__.py b/pkgbuilds/python-meh/src/python-meh-r0.43-1/meh/ui/__init__.py
new file mode 100644
index 0000000..8c174e1
--- /dev/null
+++ b/pkgbuilds/python-meh/src/python-meh-r0.43-1/meh/ui/__init__.py
@@ -0,0 +1,181 @@
+# Copyright (C) 2009 Red Hat, Inc.
+# All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#
+# Author(s): Chris Lumens
+#
+
+class AbstractIntf(object):
+ """The base class for Interfaces. This is an abstract class. It
+ provides methods that create and run various kinds of windows.
+ We provide a basic level of graphical and text oriented interfaces
+ here, but it should be possible for things like anaconda to supply
+ their own much more complicated ones with little effort.
+ """
+ def __init__(self, *args, **kwargs):
+ """Create a new AbstractInterface instance. This method need not
+ be overridden by subclasses.
+ """
+ pass
+
+ def enableNetwork(self, *args, **kwargs):
+ """Display and run a dialog that brings up the network, if it is
+ currently disabled. This method must be provided by all subclasses,
+ and should return False if unsuccessful.
+ """
+ raise NotImplementedError
+
+ def exitWindow(self, title, message, *args, **kwargs):
+ """Display and run a dialog that displays a message with the proper
+ buttons and images for quitting afterwards. This method must be
+ provided by all subclasses.
+
+ title -- The window's title.
+ message -- The window's text.
+ """
+ raise NotImplementedError
+
+ def mainExceptionWindow(self, text, exnFile, *args, **kwargs):
+ """Create and return an instance of the initial execption handling
+ dialog. This method must be provided by all subclasses.
+
+ text -- The short traceback (usually, just the stack trace).
+ exnFile -- A file containing the output of ExceptionDump.write().
+ """
+ raise NotImplementedError
+
+ def messageWindow(self, title, message, *args, **kwargs):
+ """Display a generic informational message. This can be an error,
+ status update, and so forth. It must be provided by all subclasses.
+
+ title -- The window's title.
+ message -- The window's text.
+ """
+ raise NotImplementedError
+
+ def saveExceptionWindow(self, exnFile, *args, **kwargs):
+ """Create and return an instance of the exception saving dialog. This
+ method must be provided by all subclasses.
+ """
+ raise NotImplementedError
+
+class AbstractMainExceptionWindow(object):
+ """This abstract class describes the basic level of support required of
+ any interface that supplies a main exception window. The main exception
+ window is the initial dialog displayed by the exception handler that
+ provides the debug/save/quit options.
+ """
+ def __init__(self, shortTraceback=None, longTraceback=None, *args, **kwargs):
+ """Create a new MainExceptionWindow instance. This must handle
+ creating the dialog and populating it with widgets, but must not
+ run the dialog. A self.dialog attribute should be created that
+ refers to the dialog itself.
+
+ shortTraceback -- The short traceback (usually, just the stack
+ trace).
+ longTraceback -- The long traceback (full traceback and the main
+ object dump).
+ """
+ pass
+
+ def destroy(self, *args, **kwargs):
+ """Destroy the current dialog. This method must be provided by all
+ subclasses.
+ """
+ raise NotImplementedError
+
+ def run(self, *args, **kwargs):
+ """Run the window and set a return value. This method does everything
+ after the interface has been set up, but does not destroy it. This
+ method must be provided by all subclasses.
+ """
+ raise NotImplementedError
+
+class AbstractExitWindow(object):
+ """This abstract class describes the basic level of support required of
+ a basic message dialog that should be displayed before the program
+ exits. All interfaces must provide this class.
+ """
+ def __init__(self, title, text, *args, **kwargs):
+ """Create a new ExitWindow instance. This must handle creating the
+ dialog and putting the text into it, but must not run the dialog.
+ This method must be provided by all subclasses.
+
+ title -- The window's title.
+ text -- The window's text.
+ """
+ pass
+
+ def destroy(self, *args, **kwargs):
+ """Destroy the current dialog. This method must be provided by all
+ subclasses.
+ """
+ raise NotImplementedError
+
+ def run(self, *args, **kwargs):
+ """Run the window and set a return value. This method does everything
+ after the interface has been set up but does not destroy it. This
+ method must be provided by all subclasses.
+ """
+ raise NotImplementedError
+
+class AbstractMessageWindow(object):
+ """This abstract class describes the basic level of support required of
+ a basic message dialog. All interfaces must provide this class.
+ """
+ def __init__(self, title, text, *args, **kwargs):
+ """Create a new MessageWindow instance. This must handle creating the
+ dialog and putting the text into it, but must not run the dialog.
+ This method must be provided by all subclasses.
+
+ title -- The window's title.
+ text -- The window's text.
+ """
+ pass
+
+ def destroy(self, *args, **kwargs):
+ """Destroy the current dialog. This method must be provided by all
+ subclasses.
+ """
+ raise NotImplementedError
+
+ def run(self, *args, **kwargs):
+ """Run the window and set a return value. This method does everything
+ after the interface has been set up but does not destroy it. This
+ method must be provided by all subclasses.
+ """
+ raise NotImplementedError
+
+class AbstractSaveExceptionWindow(object):
+ """This abstract class describes the basic level of support required of
+ any interface that supplies an exception saving window. The exception
+ saving window is the dialog displayed if the user clicks "save" on the
+ initial dialog, and presents multiple options for how the traceback
+ should be saved.
+ """
+ def __init__(self, *args, **kwargs):
+ """Create a new SaveExceptionWindow instance. This must handle
+ creating the dialog and populating it with widgets, but must not
+ run the dialog. A self.dialog attribute should be created that
+ refers to the dialog itself.
+ """
+ pass
+
+ def run(self, *args, **kwargs):
+ """Run the window and set a return value. This method does everything
+ after the interface has been set up but does not destroy it. This
+ method must be provided by all subclasses.
+ """
+ raise NotImplementedError
diff --git a/pkgbuilds/python-meh/src/python-meh-r0.43-1/meh/ui/gui.py b/pkgbuilds/python-meh/src/python-meh-r0.43-1/meh/ui/gui.py
new file mode 100644
index 0000000..b6920a3
--- /dev/null
+++ b/pkgbuilds/python-meh/src/python-meh-r0.43-1/meh/ui/gui.py
@@ -0,0 +1,177 @@
+# Copyright (C) 2009 Red Hat, Inc.
+# All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#
+# Author(s): Chris Lumens
+#
+from meh import MAIN_RESPONSE_DEBUG, MAIN_RESPONSE_NONE, MAIN_RESPONSE_QUIT, MAIN_RESPONSE_SAVE
+from meh.ui import AbstractIntf, AbstractSaveExceptionWindow, AbstractMainExceptionWindow, AbstractMessageWindow
+import os
+import sys
+import report
+
+import gi
+gi.require_version("Gtk", "3.0")
+
+from gi.repository import Gtk
+
+import gettext
+_ = lambda x: gettext.ldgettext("python-meh", x)
+
+def find_glade_file(file):
+ path = os.environ.get("GLADEPATH", "./:ui/:/tmp/updates/:/tmp/updates/ui/:/usr/share/python-meh/")
+ for d in path.split(":"):
+ fn = d + file
+ if os.access(fn, os.R_OK):
+ return fn
+ raise RuntimeError("Unable to find glade file %s" % file)
+
+class GraphicalIntf(AbstractIntf):
+ def __init__(self, *args, **kwargs):
+ AbstractIntf.__init__(self, *args, **kwargs)
+
+ def enableNetwork(self, *args, **kwargs):
+ """Should be provided by the inheriting class."""
+
+ return False
+
+ def exitWindow(self, title, message, *args, **kwargs):
+ win = ExitWindow(title, message, *args, **kwargs)
+ win.run()
+ win.destroy()
+
+ def mainExceptionWindow(self, text, exnFile, *args, **kwargs):
+ win = MainExceptionWindow(text, exnFile, *args, **kwargs)
+ return win
+
+ def messageWindow(self, title, message, *args, **kwargs):
+ win = MessageWindow(title, message, *args, **kwargs)
+ win.run()
+ win.destroy()
+
+ def saveExceptionWindow(self, signature, *args, **kwargs):
+ win = SaveExceptionWindow(signature)
+ win.run()
+
+class SaveExceptionWindow(AbstractSaveExceptionWindow):
+ def __init__(self, signature, *args, **kwargs):
+ AbstractSaveExceptionWindow.__init__(self, signature, *args, **kwargs)
+ self.signature = signature
+
+ def run(self, *args, **kwargs):
+ # Don't need to check the return value of report since it will
+ # handle all the UI reporting for us.
+ report.report_problem_in_memory(self.signature, report.LIBREPORT_WAIT)
+
+class MainExceptionWindow(AbstractMainExceptionWindow):
+ def __init__(self, shortTraceback=None, longTraceback=None, *args, **kwargs):
+ AbstractMainExceptionWindow.__init__(self, shortTraceback, longTraceback,
+ *args, **kwargs)
+
+ builder = Gtk.Builder()
+ builder.set_translation_domain("python-meh")
+ glade_file = find_glade_file("exception-dialog.glade")
+ builder.add_from_file(glade_file)
+ builder.connect_signals(self)
+
+ self._main_window = builder.get_object("exceptionWindow")
+
+ # this makes sure the Meh window remains active
+ # even if the exception has been triggered in a dialog
+ window_group = Gtk.WindowGroup()
+ window_group.add_window(self._main_window)
+
+ self._traceback_buffer = builder.get_object("tracebackBuffer")
+
+ self._traceback_buffer.set_text(longTraceback)
+ self._response = MAIN_RESPONSE_QUIT
+
+ self._debug_button = builder.get_object("debugButton")
+
+ allowDebug = kwargs.get("allowDebug", sys.stdout.isatty)
+
+ if allowDebug and not allowDebug():
+ self._debug_button.set_sensitive(False)
+
+ @property
+ def main_window(self):
+ return self._main_window
+
+ def destroy(self, *args, **kwargs):
+ self._main_window.destroy()
+
+ def run(self, *args, **kwargs):
+ self._main_window.show_all()
+ # keep our window above dialogs
+ self._main_window.set_modal(True)
+ self._main_window.set_keep_above(True)
+ Gtk.main()
+ self.destroy()
+ return self._response
+
+ def on_report_clicked(self, button):
+ # hide our window to not interfere with libreport window
+ self._main_window.hide()
+ self._response = MAIN_RESPONSE_SAVE
+ Gtk.main_quit()
+
+ def on_quit_clicked(self, button):
+ self._response = MAIN_RESPONSE_QUIT
+ Gtk.main_quit()
+
+ def on_debug_clicked(self, button):
+ self._response = MAIN_RESPONSE_DEBUG
+ Gtk.main_quit()
+
+ def on_expander_activated(self, expander, *args):
+ if not expander.get_expanded():
+ self._main_window.resize(600, 400)
+ else:
+ #resize the window back to the default size when expander is
+ #minimized
+ self._main_window.resize(600, 1)
+
+ def on_main_window_deleted(self, *args):
+ self._response = MAIN_RESPONSE_NONE
+ self.destroy()
+ Gtk.main_quit()
+
+class MessageWindow(AbstractMessageWindow):
+ def __init__(self, title, text, *args, **kwargs):
+ AbstractMessageWindow.__init__(self, title, text, *args, **kwargs)
+ self.rc = 0
+
+ self.dialog = Gtk.MessageDialog(buttons=Gtk.ButtonsType.OK,
+ type=Gtk.MessageType.INFO,
+ message_format=text)
+ self.dialog.set_title(title)
+ self.dialog.set_position(Gtk.WindowPosition.CENTER)
+
+ def destroy(self, *args, **kwargs):
+ self.dialog.destroy()
+
+ def run(self, *args, **kwargs):
+ self.dialog.show_all()
+ self.rc = self.dialog.run()
+ self.dialog.destroy()
+
+class ExitWindow(MessageWindow):
+ def __init__(self, title, text, *args, **kwargs): # pylint: disable=super-init-not-called
+ self.dialog = Gtk.MessageDialog(buttons=Gtk.ButtonsType.NONE,
+ type=Gtk.MessageType.INFO,
+ message_format=text)
+ self.dialog.set_title(title)
+ self.dialog.add_button(_("_Exit"), 0)
+ self.dialog.set_position(Gtk.WindowPosition.CENTER)
diff --git a/pkgbuilds/python-meh/src/python-meh-r0.43-1/meh/ui/text.py b/pkgbuilds/python-meh/src/python-meh-r0.43-1/meh/ui/text.py
new file mode 100644
index 0000000..e4d99f2
--- /dev/null
+++ b/pkgbuilds/python-meh/src/python-meh-r0.43-1/meh/ui/text.py
@@ -0,0 +1,225 @@
+# Copyright (C) 2009, 2013 Red Hat, Inc.
+# All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#
+# Author(s): Chris Lumens
+# Vratislav Podzimek
+#
+from __future__ import print_function
+
+from meh import MAIN_RESPONSE_DEBUG, MAIN_RESPONSE_SAVE, MAIN_RESPONSE_SHELL, MAIN_RESPONSE_QUIT
+from meh.ui import AbstractIntf, AbstractSaveExceptionWindow, AbstractMainExceptionWindow, AbstractMessageWindow
+import report
+import report.io.TextIO
+from report import LIBREPORT_WAIT, LIBREPORT_RUN_CLI
+
+import os
+import sys
+if sys.version_info.major == 3:
+ raw_input_fn = input
+else:
+ raw_input_fn = raw_input # pylint: disable=undefined-variable
+
+import gettext
+_ = lambda x: gettext.ldgettext("python-meh", x)
+
+
+class IOHandler(object):
+ """
+ Class that provides methods for input and output. Its instance is expected
+ to be passed to objects performing some I/O operations.
+
+ """
+
+ def __init__(self, in_func=raw_input_fn, out_func=print): # pylint: disable=used-before-assignment
+ """
+ Constructor for the IOhandler class. Arguments can be used to override
+ default I/O functions with the custom ones.
+
+ :param in_func: input function similar to standard raw_input
+ :type in_func: str -> str
+ :param out_func: output function similar to standard print
+ :type out_func: str -> None
+
+ """
+
+ self.in_func = in_func
+ self.out_func = out_func
+
+ def print(self, msg=""):
+ self.out_func(msg)
+
+ def raw_input(self, prompt):
+ return self.in_func(prompt)
+
+class TextWindow(object):
+ """Helper class providing some common methods needed by all text windows."""
+
+ def __init__(self, title, *args, **kwargs):
+ self._io = kwargs.get("io_handler", IOHandler())
+ self._title = title
+
+ # short (one-char) answer meaning "yes"
+ self._yes_answer = _("y")
+
+ # short (one-char) answer meaning "no"
+ self._no_answer = _("n")
+
+ @property
+ def _usable_width(self):
+ return os.environ.get("COLUMNS", 80) - 1
+
+ def _print_rule(self):
+ rule = self._usable_width * "="
+ self._io.print(rule)
+
+ def print_header(self):
+ self._io.print()
+ self._print_rule()
+ self._io.print(self._title)
+ self._print_rule()
+
+ def destroy(self):
+ self._print_rule()
+ self._print_rule()
+
+class TextIntf(AbstractIntf):
+ def __init__(self, *args, **kwargs):
+ AbstractIntf.__init__(self, *args, **kwargs)
+ self.screen = kwargs.get("screen", None)
+ self._io = kwargs.get("io_handler", IOHandler())
+
+ def set_io_handler(self, handler):
+ """
+ Set different IO handler.
+
+ :type handler: an instance of the IOHandler class
+
+ """
+ self._io = handler
+
+ def enableNetwork(self, *args, **kwargs):
+ """Should be provided by the inheriting class."""
+
+ return False
+
+ def exitWindow(self, title, message, *args, **kwargs):
+ kwargs["io_handler"] = self._io
+ win = ExitWindow(title, message, *args, **kwargs)
+ win.run()
+ win.destroy()
+
+ def mainExceptionWindow(self, text, exnFile, *args, **kwargs):
+ kwargs["io_handler"] = self._io
+ win = MainExceptionWindow(text, exnFile, *args, **kwargs)
+ return win
+
+ def messageWindow(self, title, message, *args, **kwargs):
+ kwargs["io_handler"] = self._io
+ win = MessageWindow(title, message, *args, **kwargs)
+ win.run()
+ win.destroy()
+
+ def saveExceptionWindow(self, signature, *args, **kwargs):
+ kwargs["io_handler"] = self._io
+ win = SaveExceptionWindow(signature, *args, **kwargs)
+ win.run()
+ win.destroy()
+
+class SaveExceptionWindow(TextWindow, AbstractSaveExceptionWindow):
+ def __init__(self, signature, *args, **kwargs):
+ AbstractSaveExceptionWindow.__init__(self, signature,
+ *args, **kwargs)
+ TextWindow.__init__(self, _("Save exception"), *args, **kwargs)
+ self.signature = signature
+
+ def run(self, *args, **kwargs):
+ # Don't need to check the return value of report since it will
+ # handle all the UI reporting for us.
+ report.report_problem_in_memory(self.signature,
+ LIBREPORT_WAIT|LIBREPORT_RUN_CLI)
+
+class MainExceptionWindow(TextWindow, AbstractMainExceptionWindow):
+ def __init__(self, shortTraceback=None, longTraceback=None, *args, **kwargs):
+ AbstractMainExceptionWindow.__init__(self, shortTraceback, longTraceback,
+ *args, **kwargs)
+ TextWindow.__init__(self, _("An unknown error has occurred"),
+ *args, **kwargs)
+ self._short_traceback = shortTraceback
+ self._menu_items = [(_("Report Bug"), MAIN_RESPONSE_SAVE),
+ (_("Run shell"), MAIN_RESPONSE_SHELL),
+ (_("Quit"), MAIN_RESPONSE_QUIT)]
+
+ allowDebug = kwargs.get("allowDebug", sys.stdout.isatty)
+
+ if allowDebug and allowDebug():
+ self._menu_items.insert(1, (_("Debug"), MAIN_RESPONSE_DEBUG))
+
+ def run(self, *args, **kwargs):
+ self.print_header()
+ self._io.print(self._short_traceback)
+ self._io.print(_("What do you want to do now?"))
+ for (idx, item) in enumerate(self._menu_items):
+ self._io.print("%d) %s" % (idx + 1, item[0]))
+
+ ret = -1
+ num_menu_items = len(self._menu_items)
+ self._io.print()
+ while not (0 < ret <= num_menu_items):
+ ret = self._io.raw_input(_("Please make your choice from above: "))
+ try:
+ ret = int(ret)
+ except ValueError:
+ ret = -1
+
+ return self._menu_items[ret - 1][1]
+
+class MessageWindow(TextWindow, AbstractMessageWindow):
+ def __init__(self, title, text, *args, **kwargs):
+ AbstractMessageWindow.__init__(self, title, text, *args, **kwargs)
+ TextWindow.__init__(self, title, *args, **kwargs)
+ self._text = text
+
+ def run(self, *args, **kwargs):
+ self.print_header()
+ self._io.print(self._text)
+ self._io.print()
+ self._io.raw_input(_("Hit ENTER to continue"))
+
+class ExitWindow(MessageWindow):
+ def __init__(self, title, text, *args, **kwargs):
+ MessageWindow.__init__(self, title, text, *args, **kwargs)
+
+ def run(self, *args, **kwargs):
+ self.print_header()
+ self._io.print(self._text)
+ self._io.print()
+
+ # self._no_answer may be non-ascii string (simple .upper() doesn't work)
+ no_answer_upper = self._no_answer.decode("utf-8").upper().encode("utf-8")
+ answer = self._io.raw_input(_(
+ "Are you sure you want to exit? [%(yes)s/%(no)s]") %
+ { "yes": self._yes_answer,
+ "no": no_answer_upper })
+
+ # no answer means accepting the default (self._no_answer) and the answer
+ # is case insensitive (and may be non-ascii)
+ lower_answer = answer.decode("utf-8").lower().encode("utf-8")
+ answer = lower_answer or self._no_answer
+
+ if answer in (self._yes_answer, self._no_answer):
+ return answer == self._yes_answer
+ else:
+ self.run(*args, **kwargs)
diff --git a/pkgbuilds/python-meh/src/python-meh-r0.43-1/pixmaps/exception.png b/pkgbuilds/python-meh/src/python-meh-r0.43-1/pixmaps/exception.png
new file mode 100644
index 0000000..2e08889
Binary files /dev/null and b/pkgbuilds/python-meh/src/python-meh-r0.43-1/pixmaps/exception.png differ
diff --git a/pkgbuilds/python-meh/src/python-meh-r0.43-1/po/Makefile b/pkgbuilds/python-meh/src/python-meh-r0.43-1/po/Makefile
new file mode 100644
index 0000000..59b51bf
--- /dev/null
+++ b/pkgbuilds/python-meh/src/python-meh-r0.43-1/po/Makefile
@@ -0,0 +1,76 @@
+#
+# Makefile for the PO files (translation) catalog
+#
+# $Id$
+
+TOP = ../..
+
+# What is this package?
+NLSPACKAGE = python-meh
+POTFILE = $(NLSPACKAGE).pot
+INSTALL = /usr/bin/install -c
+INSTALL_DATA = $(INSTALL) -m 644
+INSTALL_DIR = /usr/bin/install -d
+
+# destination directory
+INSTALL_NLS_DIR = $(RPM_BUILD_ROOT)/usr/share/locale
+
+# PO catalog handling
+MSGMERGE = msgmerge -v
+XGETTEXT = xgettext --default-domain=$(NLSPACKAGE) \
+ --add-comments
+MSGFMT = msgfmt --statistics --verbose
+
+# What do we need to do
+POFILES = $(wildcard *.po)
+MOFILES = $(patsubst %.po,%.mo,$(POFILES))
+PYSRC = $(wildcard ../meh/*.py ../meh/ui/*.py)
+GLADEFILES = $(wildcard ../ui/*.glade)
+
+all:: update-po $(MOFILES)
+
+potfile: $(PYSRC) glade-po
+ $(XGETTEXT) -L Python --keyword=_ --keyword=N_ $(PYSRC) tmp/*.h
+ @if cmp -s $(NLSPACKAGE).po $(POTFILE); then \
+ rm -f $(NLSPACKAGE).po; \
+ else \
+ mv -f $(NLSPACKAGE).po $(POTFILE); \
+ fi; \
+
+glade-po: $(GLADEFILES)
+ rm -rf tmp/
+ for f in $(GLADEFILES); do \
+ intltool-extract --type=gettext/glade -l $$f ;\
+ done
+
+update-po: Makefile potfile refresh-po
+
+refresh-po: Makefile
+ for cat in $(POFILES); do \
+ lang=`basename $$cat .po`; \
+ if $(MSGMERGE) $$lang.po $(POTFILE) > $$lang.pot ; then \
+ mv -f $$lang.pot $$lang.po ; \
+ echo "$(MSGMERGE) of $$lang succeeded" ; \
+ else \
+ echo "$(MSGMERGE) of $$lang failed" ; \
+ rm -f $$lang.pot ; \
+ fi \
+ done
+
+clean:
+ @rm -fv *mo *~ .depend
+ @rm -rf tmp
+
+install: $(MOFILES)
+ @for n in $(MOFILES); do \
+ l=`basename $$n .mo`; \
+ $(INSTALL_DIR) $(INSTALL_NLS_DIR)/$$l/LC_MESSAGES; \
+ $(INSTALL_DATA) --verbose $$n $(INSTALL_NLS_DIR)/$$l/LC_MESSAGES/$(NLSPACKAGE).mo; \
+ done
+
+%.mo: %.po
+ $(MSGFMT) -o $@ $<
+
+.PHONY: missing depend
+
+
diff --git a/pkgbuilds/python-meh/src/python-meh-r0.43-1/po/python-meh.pot b/pkgbuilds/python-meh/src/python-meh-r0.43-1/po/python-meh.pot
new file mode 100644
index 0000000..e00f088
--- /dev/null
+++ b/pkgbuilds/python-meh/src/python-meh-r0.43-1/po/python-meh.pot
@@ -0,0 +1,116 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR , YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2015-10-05 14:50+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME \n"
+"Language-Team: LANGUAGE \n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=CHARSET\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: ../meh/handler.py:195
+msgid ""
+"Use 'continue' command to quit the debugger and get back to the main menu"
+msgstr ""
+
+#: ../meh/handler.py:211
+msgid "Exit the shell to get back to the main menu"
+msgstr ""
+
+#. short (one-char) answer meaning "yes"
+#: ../meh/ui/text.py:75
+msgid "y"
+msgstr ""
+
+#. short (one-char) answer meaning "no"
+#: ../meh/ui/text.py:78
+msgid "n"
+msgstr ""
+
+#: ../meh/ui/text.py:145
+msgid "Save exception"
+msgstr ""
+
+#: ../meh/ui/text.py:158 tmp/exception-dialog.glade.h:1
+msgid "An unknown error has occurred"
+msgstr ""
+
+#: ../meh/ui/text.py:161
+msgid "Report Bug"
+msgstr ""
+
+#: ../meh/ui/text.py:162
+msgid "Run shell"
+msgstr ""
+
+#: ../meh/ui/text.py:163
+msgid "Quit"
+msgstr ""
+
+#: ../meh/ui/text.py:168
+msgid "Debug"
+msgstr ""
+
+#: ../meh/ui/text.py:173
+msgid "What do you want to do now?"
+msgstr ""
+
+#: ../meh/ui/text.py:181
+msgid "Please make your choice from above: "
+msgstr ""
+
+#: ../meh/ui/text.py:199
+msgid "Hit ENTER to continue"
+msgstr ""
+
+#: ../meh/ui/text.py:213
+#, python-format
+msgid "Are you sure you want to exit? [%(yes)s/%(no)s]"
+msgstr ""
+
+#: ../meh/ui/gui.py:176
+msgid "_Exit"
+msgstr ""
+
+#: tmp/exception-dialog.glade.h:2
+msgid ""
+"This program has encountered an unknown error. You may\n"
+"report the bug below or quit the program."
+msgstr ""
+
+#: tmp/exception-dialog.glade.h:4
+msgid "_Report Bug"
+msgstr ""
+
+#: tmp/exception-dialog.glade.h:5
+msgid "_Quit"
+msgstr ""
+
+#: tmp/exception-dialog.glade.h:6
+msgid "The output below may help determine the cause of the error:"
+msgstr ""
+
+#: tmp/exception-dialog.glade.h:7
+msgid "'Debug' may take you to tty1."
+msgstr ""
+
+#: tmp/exception-dialog.glade.h:8
+msgid "_Debug"
+msgstr ""
+
+#: tmp/exception-dialog.glade.h:9
+msgid "More _info..."
+msgstr ""
+
+#: tmp/exception-dialog.glade.h:10
+msgid "No additional info available."
+msgstr ""
diff --git a/pkgbuilds/python-meh/src/python-meh-r0.43-1/python-meh.spec b/pkgbuilds/python-meh/src/python-meh-r0.43-1/python-meh.spec
new file mode 100644
index 0000000..1ef255c
--- /dev/null
+++ b/pkgbuilds/python-meh/src/python-meh-r0.43-1/python-meh.spec
@@ -0,0 +1,383 @@
+%{!?python_sitelib: %define python_sitelib %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")}
+%global with_python3 1
+
+%define libreportver 2.0.18-1
+
+Summary: A python library for handling exceptions
+Name: python-meh
+Url: https://github.com/rhinstaller/python-meh
+Version: 0.43
+Release: 1%{?dist}
+# This is a Red Hat maintained package which is specific to
+# our distribution. Thus the source is only available from
+# within this srpm.
+# This tarball was created from upstream git:
+# git clone https://github.com/rhinstaller/python-meh
+# cd python-meh && make archive
+Source0: https://github.com/rhinstaller/python-meh/archive/%{name}-%{version}.tar.gz
+
+License: GPLv2+
+Group: System Environment/Libraries
+BuildArch: noarch
+BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
+BuildRequires: python-devel
+BuildRequires: gettext
+BuildRequires: python-setuptools
+BuildRequires: intltool
+BuildRequires: dbus-python
+BuildRequires: libreport-gtk >= %{libreportver}
+BuildRequires: libreport-cli >= %{libreportver}
+BuildRequires: libreport-python >= %{libreportver}
+BuildRequires: python-six
+
+%if 0%{with_python3}
+BuildRequires: python3-devel
+BuildRequires: python3-setuptools
+BuildRequires: python3-dbus
+BuildRequires: libreport-python3 >= %{libreportver}
+BuildRequires: python3-six
+BuildRequires: python3-pocketlint
+%endif
+
+Requires: python
+Requires: dbus-python
+Requires: rpm-python
+Requires: libreport-cli >= %{libreportver}
+Requires: libreport-python >= %{libreportver}
+Requires: python-six
+
+%description
+The python-meh package is a python library for handling, saving, and reporting
+exceptions.
+
+%package gui
+Summary: Graphical user interface for the python-meh library
+Requires: python-meh = %{version}-%{release}
+Requires: pygobject3
+Requires: gtk3
+Requires: libreport-gtk >= %{libreportver}
+
+%description gui
+The python-meh-gui package provides a GUI for the python-meh library.
+
+%if 0%{with_python3}
+%package -n python3-meh
+Summary: A python 3 library for handling exceptions
+Requires: python3
+Requires: python3-dbus
+Requires: rpm-python3
+Requires: libreport-cli >= %{libreportver}
+Requires: libreport-python3 >= %{libreportver}
+Requires: python3-six
+
+%description -n python3-meh
+The python3-meh package is a python 3 library for handling, saving, and reporting
+exceptions.
+
+%package -n python3-meh-gui
+Summary: Graphical user interface for the python3-meh library
+Requires: python3-meh = %{version}-%{release}
+Requires: python3-gobject, gtk3
+Requires: libreport-gtk >= %{libreportver}
+
+%description -n python3-meh-gui
+The python3-meh-gui package provides a GUI for the python3-meh library.
+
+%endif
+
+%prep
+%setup -q
+
+%if 0%{?with_python3}
+rm -rf %{py3dir}
+cp -a . %{py3dir}
+%endif # with_python3
+
+%build
+make
+
+%if 0%{?with_python3}
+pushd %{py3dir}
+make PYTHON=%{__python3}
+popd
+%endif
+
+%check
+make test
+
+%if 0%{?with_python3}
+pushd %{py3dir}
+# Needs UTF-8 locale
+LANG=en_US.UTF-8 make PYTHON=%{__python3} test
+popd
+%endif
+
+%install
+rm -rf %{buildroot}
+make DESTDIR=%{buildroot} install
+
+%if 0%{?with_python3}
+pushd %{py3dir}
+make PYTHON=%{__python3} DESTDIR=%{buildroot} install
+popd
+%endif
+
+%find_lang %{name}
+
+%clean
+rm -rf %{buildroot}
+
+%files -f %{name}.lang
+%defattr(-,root,root,-)
+%doc ChangeLog COPYING
+%{python_sitelib}/*
+%exclude %{python_sitelib}/meh/ui/gui.py*
+
+%files gui
+%{python_sitelib}/meh/ui/gui.py*
+%{_datadir}/python-meh
+
+%files -n python3-meh
+%doc ChangeLog COPYING
+%{python3_sitelib}/*
+%exclude %{python3_sitelib}/meh/ui/gui.py*
+
+%files -n python3-meh-gui
+%{python3_sitelib}/meh/ui/gui.py*
+%{_datadir}/python-meh
+
+%changelog
+* Mon Oct 05 2015 Martin Kolman - 0.43-1
+- Set translations domain for glade files (vtrefny)
+
+* Tue Sep 29 2015 Martin Kolman - 0.42-1
+- Add tests and ChangeLog to MANIFEST.in (vtrefny)
+- Fix clicking on buttons on error pop-up doesn't work (#1250942) (vtrefny)
+- Add Makefile to MANIFEST.in (vtrefny)
+- Add archive target to Makefile (vtrefny)
+- Add .gitignore (vtrefny)
+
+* Wed Sep 16 2015 Martin Kolman - 0.41-1
+- Make sure the Meh window has focus even if a dialog is displayed (mkolman)
+- Run pylint as part of "make ci". (clumens)
+- Add a new makefile target that does everything needed for jenkins. (clumens)
+- Fix the other pylint-discovered errors. (clumens)
+- Use isinstance instead of type. (clumens)
+- Fix all the places we're redefining something. (clumens)
+- Don't use [] as the default argument in a method. (clumens)
+- Take care of exception-related pylint messages. (clumens)
+- Get rid of all the unused variable warnings. (clumens)
+- Get rid of all unused and wildcard imports. (clumens)
+- Add pocketlint support to python-meh. (clumens)
+- Use gi.require_version for Gtk import (vtrefny)
+
+* Tue Apr 28 2015 Martin Kolman - 0.40-1
+- Make sure the date in RPM changelog is always in English (mkolman)
+- Update upstream URL (mkolman)
+
+* Wed Apr 01 2015 Martin Kolman - 0.39-1
+- Handle LANG=C (mkolman)
+
+* Tue Mar 31 2015 Martin Kolman - 0.38-1
+- Switch to Zanata for translations (mkolman)
+- Run both Python 2 and Python 3 tests for "make test" (mkolman)
+- Python 3 compatibility fixes for the Unicode test (mkolman)
+- Make it possible to specify file open mode (mkolman)
+- Add missing six dependency (mkolman)
+- Fix date in changelog (mkolman)
+
+* Fri Mar 27 2015 Martin Kolman - 0.37-1
+- Fix dumping file attachments in Python 3 (vtrefny)
+- meh has not required openssh-clients since 2009 (awilliam)
+
+* Thu Mar 05 2015 Martin Kolman - 0.36-1
+- Package's epoch returned by RPM is an integer (#1199263) (vpodzime)
+
+* Tue Feb 17 2015 Martin Kolman - 0.35-1
+- Add option to remove the 'Debug' option/button for mainExceptionWindow (vtrefny)
+
+* Tue Dec 09 2014 Vratislav Podzimek - 0.34-1
+- Encode str/unicode object before hashing it (vpodzime)
+- Use dict.items() instead of dict.iteritems() (vpodzime)
+- Add one more flag to test (for Python3) (vpodzime)
+- Make sure we work with strings when we think we do (vpodzime)
+
+* Fri Dec 05 2014 Vratislav Podzimek - 0.33-1
+- Make sure fresh translations are always fetched (vpodzime)
+- Add pieces needed to build the python3- subpackage (#985294) (mhroncok)
+- Remove yum from requires and fix setuptools requires (mhroncok)
+- raw_input is replaced by input in Python3 (vpodzime)
+- Raise exception in a Python3 compatible way (mhroncok)
+- Remove the --disable-overwrite parameter for the Transifex client (mkolman)
+- Use /usr/bin/python2 in scripts (mkolman)
+- Add example code using python-meh (vpodzime)
+
+* Mon Apr 28 2014 Martin Kolman - 0.32-1
+- Translation update
+
+* Fri Mar 21 2014 Martin Kolman - 0.31-1
+- Translation update
+
+* Wed Dec 18 2013 Vratislav Podzimek - 0.30-1
+- Exclude compiled versions of gui.py from the non-gui package (dshea)
+- Skip callbacks providing no information (vpodzime)
+
+* Wed Nov 20 2013 Vratislav Podzimek - 0.29-1
+- Split GUI out into a separate package (vpodzime)
+- Create archives in one Makefile target and reuse it (vpodzime)
+
+* Tue Nov 05 2013 Vratislav Podzimek - 0.28-1
+- Introduce support for Python 3 while keeping Python 2 working (miro)
+- Sync spec with downstream (vpodzime)
+
+* Wed Oct 09 2013 Vratislav Podzimek - 0.27-1
+- Use join method instead of the joinfields function
+- Translate the hints on how to quit debugger and shell
+- Add a way to run shell when exception appears
+- Get rid of constants that are no longer used anywhere
+- Filter local variables in a nicer way and fix docstring
+
+* Sun Aug 04 2013 Fedora Release Engineering - 0.26-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_20_Mass_Rebuild
+
+* Tue Jul 23 2013 Vratislav Podzimek - 0.26-1
+- Output binary data correctly as hexa strings (#986515) (vpodzime)
+- Add newline before dumping callbacks' outputs (vpodzime)
+
+* Tue Jun 18 2013 Vratislav Podzimek - 0.25-1
+- Add and use the safe_string module and the SafeStr class (vpodzime)
+- Give translators hint about the cryptic strings (vpodzime)
+
+* Thu May 02 2013 Vratislav Podzimek - 0.24-1
+- Epoch of the package from RPM db can be None (#957789) (vpodzime)
+
+* Wed Apr 17 2013 Vratislav Podzimek - 0.23-1
+- Use Sphinx syntax for docstrings (vpodzime)
+- Allow a change of the I/O functions (vpodzime)
+- Reword the 'Debug' button warning (#948256) (vpodzime)
+
+* Thu Apr 04 2013 Vratislav Podzimek - 0.22-1
+- Some more stuff for ABRT/libreport (#929181) (vpodzime)
+- Tell ABRT we are reporting a Python excetion (vpodzime)
+- Use named tuples instead of our magic tuples (vpodzime)
+
+* Thu Feb 14 2013 Fedora Release Engineering - 0.21-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_19_Mass_Rebuild
+
+* Mon Jan 21 2013 Vratislav Podzimek - 0.21-1
+- New purely textual text interface (vpodzime)
+- Add dumb enableNetwork implementation to GraphicalIntf (vpodzime)
+- Destroy main window after running it (vpodzime)
+- Get rid of the accountManager and use new libreport API (vpodzime)
+- Add a way to override previously registered callback (vpodzime)
+- Allow callbacks marked as attachment only (vpodzime)
+- Add support for callbacks providing additional data (vpodzime)
+
+* Tue Dec 11 2012 Vratislav Podzimek - 0.20-1
+- Handle non-ascii dict keys and values correctly (#883641) (vpodzime)
+
+* Wed Nov 14 2012 Vratislav Podzimek - 0.19-1
+- Add test for handling unicode strings and files (vpodzime)
+- Read files as UTF-8 and ignore errors (#874250) (vpodzime)
+- Add %check section to the spec file (vpodzime)
+- Fix tests (vpodzime)
+
+* Thu Oct 25 2012 Vratislav Podzimek - 0.18-1
+- Handle tracebacks with no stack (#866441) (vpodzime)
+- Parse component name correctly (#866526) (vpodzime)
+- Spelling corrections (#865993) (vpodzime)
+
+* Tue Oct 09 2012 Vratislav Podzimek - 0.17-1
+- Handle unicode strings correctly (#854959) (vpodzime)
+
+* Tue Sep 11 2012 Vratislav Podzimek - 0.16-1
+- Do not overwrite process information with files having the same basename (vpodzime)
+- Encode dump as utf-8 before writing to file (#854959) (vpodzime)
+
+* Mon Aug 20 2012 Vratislav Podzimek - 0.15-1
+- Add main_window property to the MainExceptionWindow (vpodzime)
+- Don't try to dump objects without __dict__ (vpodzime)
+- Change require from rpm to rpm-python (vpodzime)
+
+* Fri Aug 03 2012 Vratislav Podzimek - 0.14-1
+- Use just a basename of the attached file as the item name (vpodzime)
+- Set the type hint for the mainExceptionWindow to Dialog (vpodzime)
+- Store and then write out the string representation of the traceback and object dump (vpodzime)
+
+* Fri Jul 27 2012 Vratislav Podzimek - 0.13-1
+- Add files specified in the Config object as attachments to bugreports (vpodzime)
+- Display hint how to quit the debugger (vpodzime)
+- Do not kill the process when 'continue' is used in pdb (vpodzime)
+- Port to Gtk3 and the new design (vpodzime)
+- Remove the rc attribute and getrc methods (vpodzime)
+- Fix 'all' and 'install' Makefile targets (vpodzime)
+- Migrate l10n to Transifex (vpodzime)
+
+* Sat Jul 21 2012 Fedora Release Engineering - 0.12-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild
+
+* Tue Dec 20 2011 Vratislav Podzimek 0.12
+- Use new libreport API to get more information to bugzilla (vpodzime).
+- Adapt to the new API of libreport (vpodzime).
+- Move "import rpm" to where it's needed to avoid nameserver problems (clumens).
+ Resolves: rhbz#749330
+- Change dependency to libreport-* (mtoman).
+ Resolves: rhbz#730924
+- Add abrt-like information to bug reports (vpodzime).
+ Resolves: rhbz#728871
+- Propagate the screen attr when using text mode (jmoskovc).
+
+* Tue Feb 08 2011 Fedora Release Engineering - 0.11-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild
+
+* Tue Jan 25 2011 Chris Lumens - 0.11-1
+- Update the spec file URL to something valid (#670601). (clumens)
+- Don't use _D for Debug, since that's already used by the expander (#640929). (clumens)
+- Translation updates.
+
+* Tue Jun 22 2010 Chris Lumens 0.10-1
+- Treat classes like simple types, too. (clumens)
+
+* Thu Jun 10 2010 Chris Lumens - 0.9-1
+- Remove the requirement on python-bugzilla (#602794). (clumens)
+- Rename ba.po -> bs.po (#583055). (clumens)
+- Translation updates.
+
+* Thu Mar 04 2010 Chris Lumens - 0.8-1
+- And add a requirement for report as well. (clumens)
+- filer.py is now completely taken over by meh. (clumens)
+- Everything from savers.py has moved into report. (clumens)
+- Remove unused UI code now that report handles all this for me. (clumens)
+- Switch ExceptionHandler to use report (#562656). (clumens)
+- Don't allow an exception when writing out an attribute stop the whole dump. (clumens)
+- Credit where credit is due. (clumens)
+
+* Tue Nov 03 2009 Chris Lumens - 0.7-1
+- Add a test case framework.
+- Move src -> meh for ease of test case writing.
+- Another attempt at making the attrSkipList work (#532612, #532737).
+
+* Thu Oct 08 2009 Chris Lumens - 0.6-1
+- Make idSkipList work again.
+- Support dumping objects derived from Python's object.
+- Use the right method to set text on a snack.Entry (#526884).
+
+* Tue Sep 29 2009 Chris Lumens - 0.5-1
+- Always compare version numbers as strings (#526188).
+
+* Fri Sep 25 2009 Chris Lumens - 0.4-1
+- Add a default description to bug reports.
+- Handle the user pressing Escape by continuing to show the dialog.
+- Lots more translation updates.
+
+* Thu Sep 10 2009 Chris Lumens - 0.3-1
+- Pull in lots of new translations (#522410).
+
+* Wed Aug 19 2009 Chris Lumens - 0.2-1
+- Add a title to the main exception dialog so it looks right in anaconda.
+- Don't include an extra '/' in the displayed bug URL (#517515).
+- Now that there's .po files, package them.
+- Use the new exception icon (#517164).
+
+* Tue Jul 28 2009 Chris Lumens - 0.1-1
+- Initial package.
diff --git a/pkgbuilds/python-meh/src/python-meh-r0.43-1/setup.py b/pkgbuilds/python-meh/src/python-meh-r0.43-1/setup.py
new file mode 100644
index 0000000..ee7f066
--- /dev/null
+++ b/pkgbuilds/python-meh/src/python-meh-r0.43-1/setup.py
@@ -0,0 +1,10 @@
+#!/usr/bin/python2
+
+from distutils.core import setup
+
+setup(name='python-meh', version='0.43',
+ description='Python module for handling exceptions',
+ author='Chris Lumens', author_email='clumens@redhat.com',
+ url='http://git.fedoraproject.org/git/?p=python-meh.git',
+ data_files = [('/usr/share/python-meh', ['ui/exception-dialog.glade'])],
+ packages=['meh', 'meh.ui'])
diff --git a/pkgbuilds/python-meh/src/python-meh-r0.43-1/tests/__init__.py b/pkgbuilds/python-meh/src/python-meh-r0.43-1/tests/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/pkgbuilds/python-meh/src/python-meh-r0.43-1/tests/attrSkipList.py b/pkgbuilds/python-meh/src/python-meh-r0.43-1/tests/attrSkipList.py
new file mode 100644
index 0000000..b21c6d1
--- /dev/null
+++ b/pkgbuilds/python-meh/src/python-meh-r0.43-1/tests/attrSkipList.py
@@ -0,0 +1,25 @@
+import unittest
+from tests.baseclass import BaseTestCase
+
+from meh import Config
+
+class Example:
+ def __init__(self):
+ self.rootPassword = "blahblah"
+ self.dontSkipMe = 12345
+
+class AttrSkipList_TestCase(BaseTestCase):
+ def runTest(self):
+ example = Example()
+
+ conf = Config(programName="test",
+ programVersion="47",
+ attrSkipList=[ "rootPassword" ])
+
+ dump = self.dump(conf, example)
+
+ self.assertIn("rootPassword: Skipped", dump)
+ self.assertNotIn("dontSkipMe: Skipped", dump)
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/pkgbuilds/python-meh/src/python-meh-r0.43-1/tests/baseclass.py b/pkgbuilds/python-meh/src/python-meh-r0.43-1/tests/baseclass.py
new file mode 100644
index 0000000..d242ce3
--- /dev/null
+++ b/pkgbuilds/python-meh/src/python-meh-r0.43-1/tests/baseclass.py
@@ -0,0 +1,103 @@
+import glob
+import importlib
+import os
+import sys
+import tempfile
+import unittest
+import six
+
+from meh import ExceptionInfo
+from meh.dump import ExceptionDump
+
+class BaseTestCase(unittest.TestCase):
+ def dump(self, conf, obj):
+ from inspect import stack as _stack
+ stack = _stack()[1:]
+
+ dump = ExceptionDump(ExceptionInfo(None, None, stack), conf)
+ return dump.dump(obj)
+
+ def openFile(self, mode="w"):
+ (fd, path) = tempfile.mkstemp()
+ # only Python 3 has supports the "encoding" keyword argument
+ if six.PY2:
+ fo = os.fdopen(fd, mode)
+ else:
+ fo = os.fdopen(fd, mode, encoding="utf-8")
+
+ return (fo, path)
+
+ def tracebackContains(self, path, s):
+ f = open(path)
+ while True:
+ l = f.readline()
+ if l == "":
+ f.close()
+ return False
+
+ if l.find(s) != -1:
+ f.close()
+ return True
+
+ f.close()
+ return False
+
+def loadModules(moduleDir, cls_pattern="_TestCase", skip_list=None):
+ '''taken from firstboot/loader.py'''
+
+ if not skip_list:
+ skip_list = ["__init__", "baseclass"]
+
+ # Guaruntee that __init__ is skipped
+ if skip_list.count("__init__") == 0:
+ skip_list.append("__init__")
+
+ tests = list()
+
+ # Make sure moduleDir is in the system path so imputil works.
+ if not moduleDir in sys.path:
+ sys.path.insert(0, moduleDir)
+
+ # Get a list of all *.py files in moduleDir
+ lst = map(lambda x: os.path.splitext(os.path.basename(x))[0],
+ glob.glob(moduleDir + "/*.py"))
+
+ # Inspect each .py file found
+ for module in lst:
+ if module in skip_list:
+ continue
+
+ # Attempt to load the found module.
+ try:
+ loaded = importlib.import_module(module)
+ except ImportError:
+ print("Error loading module %s." % module)
+ continue
+
+ # Find class names that match the supplied pattern (default: "_TestCase")
+ beforeCount = len(tests)
+ for obj in loaded.__dict__.keys():
+ if obj.endswith(cls_pattern):
+ tests.append(loaded.__dict__[obj])
+ afterCount = len(tests)
+
+ # Warn if no tests found
+ if beforeCount == afterCount:
+ print("Module %s does not contain any test cases; skipping." % module)
+ continue
+
+ return tests
+
+# Run the tests
+if __name__ == "__main__":
+
+ # Create a test suite
+ MehTestSuite = unittest.TestSuite()
+
+ # Find tests to add
+ tstList = loadModules(os.path.join(os.environ.get("PWD"), "tests/"))
+ for tst in tstList:
+ MehTestSuite.addTest(tst())
+
+ # Run tests
+ unittest.main(defaultTest="MehTestSuite")
diff --git a/pkgbuilds/python-meh/src/python-meh-r0.43-1/tests/callbacks.py b/pkgbuilds/python-meh/src/python-meh-r0.43-1/tests/callbacks.py
new file mode 100644
index 0000000..5407f12
--- /dev/null
+++ b/pkgbuilds/python-meh/src/python-meh-r0.43-1/tests/callbacks.py
@@ -0,0 +1,56 @@
+# -*- coding: utf-8 -*-
+
+from tests.baseclass import BaseTestCase
+from meh import Config, ConfigError
+
+def callback1():
+ return "This was generated by the callback1"
+
+def callback2():
+ return "This was generated by the callback2"
+
+def callback3():
+ raise RuntimeError("Testing callback raising exception")
+
+def callback4():
+ return "This was generated by the callback4"
+
+def callback5():
+ return ""
+
+class Callbacks_TestCase(BaseTestCase):
+ def runTest(self):
+ conf = Config(programName="CallbacksTest",
+ programVersion="1.0",
+ callbackDict={"callback1": (callback1, False),
+ "callback2": (callback2, False)})
+
+ # another way to register callback
+ conf.register_callback("callback3", callback2)
+
+ # callback with given item name already registered
+ with self.assertRaises(ConfigError):
+ conf.register_callback("callback3", callback3)
+
+ # should not raise exception
+ conf.register_callback("callback3", callback3, override=True)
+
+ conf.register_callback("callback4", callback4, attchmnt_only=True)
+ conf.register_callback("callback5", callback5, attchmnt_only=False)
+
+ # should not raise exception
+ dump = self.dump(conf, None)
+
+ self.assertIn("callback1:\nThis was generated by the callback1\n",
+ dump)
+ self.assertIn("callback2:\nThis was generated by the callback2\n",
+ dump)
+ self.assertIn("callback3: Caused error", dump)
+
+ # should not appear in the dump (attachment only)
+ self.assertNotIn("callback4", dump)
+
+ # should not appear in the dump (nothing provided)
+ self.assertNotIn("callback5", dump)
+
+
diff --git a/pkgbuilds/python-meh/src/python-meh-r0.43-1/tests/handle_binary.py b/pkgbuilds/python-meh/src/python-meh-r0.43-1/tests/handle_binary.py
new file mode 100644
index 0000000..fe35ed4
--- /dev/null
+++ b/pkgbuilds/python-meh/src/python-meh-r0.43-1/tests/handle_binary.py
@@ -0,0 +1,41 @@
+# -*- coding: utf-8 -*-
+
+from tests.baseclass import BaseTestCase
+from meh import Config
+
+BINARY_DATA = b"\xff\x61\xfe\xdd"
+BINARY_DATA2 = b"\xfe\x61\xff\xdd"
+BINARY_DATA3 = b"\xfe\x62\xff\xdd"
+
+class BinaryExample(object):
+ def __init__(self):
+ self.bin_data = BINARY_DATA
+
+ # crazy, but possible
+ self.dict = dict()
+ self.dict[BINARY_DATA2] = BINARY_DATA2
+
+ self.list = [BINARY_DATA3]
+
+class HandleBinary_TestCase(BaseTestCase):
+ def runTest(self):
+ binary_example = BinaryExample()
+
+ conf = Config(programName="UnicodeTest",
+ programVersion="1.0")
+
+ # should not raise exception
+ dump = self.dump(conf, binary_example)
+
+ # should contain the attribute name and hexa representation of binary
+ # data ('\x61' == 'a' which shouldn't be translated)
+ self.assertTrue("bin_data: \\xff\\x61\\xfe\\xdd\n" in dump or
+ "bin_data: b'\\xffa\\xfe\\xdd'\n" in dump)
+
+ # should contain the binary-keyed dict
+ self.assertTrue("dict: {'\\xfe\\x61\\xff\\xdd': \\xfe\\x61\\xff\\xdd" in dump or
+ "dict: {b'\\xfea\\xff\\xdd': b'\\xfea\\xff\\xdd'" in dump)
+
+ # should contain the list with binary item(s)
+ self.assertTrue("list: [\\xfe\\x62\\xff\\xdd]" in dump or
+ "list: [b'\\xfeb\\xff\\xdd']" in dump)
diff --git a/pkgbuilds/python-meh/src/python-meh-r0.43-1/tests/handle_unicode.py b/pkgbuilds/python-meh/src/python-meh-r0.43-1/tests/handle_unicode.py
new file mode 100644
index 0000000..a4e88d0
--- /dev/null
+++ b/pkgbuilds/python-meh/src/python-meh-r0.43-1/tests/handle_unicode.py
@@ -0,0 +1,55 @@
+# -*- coding: utf-8 -*-
+import six
+
+from tests.baseclass import BaseTestCase
+from meh import Config
+
+UNICODE_STR = u"řěšččšě"
+UNICODE_LINE = u"řčšřščřčš\n"
+ASCII_STR = "fsdkljfdsldfs"
+ASCII_LINE = "fdsfsdakjfdsa\n"
+
+class UnicodeExample(object):
+ def __init__(self):
+ self.ascii_str = ASCII_STR
+ self.unicode_str = UNICODE_STR
+ self.encoded_str = self.unicode_str.encode("utf-8")
+ self.unicode_dict = { u"úú" : u"áá" }
+
+class HandleUnicode_TestCase(BaseTestCase):
+ def setUp(self):
+ # write UTF-8 and ASCII files for testing
+ if six.PY2:
+ (fobj, self.uni_file_path) = self.openFile()
+ else:
+ (fobj, self.uni_file_path) = self.openFile(mode="wt")
+ try:
+ fobj.write(UNICODE_LINE)
+ except UnicodeEncodeError:
+ fobj.write(UNICODE_LINE.encode("utf-8"))
+ fobj.close()
+
+ (fobj, self.ascii_file_path) = self.openFile()
+ fobj.write(ASCII_LINE)
+ fobj.close()
+
+ def runTest(self):
+ unicode_example = UnicodeExample()
+
+ conf = Config(programName="UnicodeTest",
+ programVersion="1.0",
+ fileList=[self.uni_file_path, self.ascii_file_path])
+
+ # should not raise exception
+ dump = self.dump(conf, unicode_example)
+
+ self.assertIn("_str: " + str(UNICODE_STR.encode("utf-8")), dump)
+ self.assertIn("encoded_str: " + str(UNICODE_STR.encode("utf-8")), dump)
+ if six.PY2:
+ self.assertIn(str(UNICODE_LINE.encode("utf-8")), dump)
+ else:
+ self.assertIn(UNICODE_LINE, dump)
+
+ self.assertIn("ascii_str: " + ASCII_STR, dump)
+ self.assertIn(ASCII_LINE.rstrip(), dump)
+
diff --git a/pkgbuilds/python-meh/src/python-meh-r0.43-1/tests/pylint/runpylint.py b/pkgbuilds/python-meh/src/python-meh-r0.43-1/tests/pylint/runpylint.py
new file mode 100755
index 0000000..146d04a
--- /dev/null
+++ b/pkgbuilds/python-meh/src/python-meh-r0.43-1/tests/pylint/runpylint.py
@@ -0,0 +1,19 @@
+#!/usr/bin/python3
+
+import sys
+
+from pocketlint import PocketLintConfig, PocketLinter
+
+class MehLintConfig(PocketLintConfig):
+ @property
+ def pylintPlugins(self):
+ retval = super(MehLintConfig, self).pylintPlugins
+ retval.remove("pocketlint.checkers.eintr")
+ retval.remove("pocketlint.checkers.markup")
+ return retval
+
+if __name__ == "__main__":
+ conf = MehLintConfig()
+ linter = PocketLinter(conf)
+ rc = linter.run()
+ sys.exit(rc)
diff --git a/pkgbuilds/python-meh/src/python-meh-r0.43-1/tests/safe_string_test.py b/pkgbuilds/python-meh/src/python-meh-r0.43-1/tests/safe_string_test.py
new file mode 100755
index 0000000..e771afd
--- /dev/null
+++ b/pkgbuilds/python-meh/src/python-meh-r0.43-1/tests/safe_string_test.py
@@ -0,0 +1,44 @@
+# -*- coding: utf-8 -*-
+
+from tests.baseclass import BaseTestCase
+from meh.safe_string import SafeStr, PY
+
+class TestClass(object):
+ def __str__(self):
+ return "string representation of TestClass instance"
+
+class TestClass2():
+ pass
+
+class SafeStr_TestCase(BaseTestCase):
+ def setUp(self):
+ self.safestr = SafeStr()
+ self.unistr = u"ááááá"
+ self.enc_unistr = self.unistr.encode("utf-8")
+ self.asciistr = "aaaa"
+ self.bindata = b'\xff\xff\xfe'
+ self.test_object = TestClass()
+ self.test_object2 = TestClass2()
+
+ def runTest(self):
+ self.safestr += self.asciistr
+ self.safestr += self.enc_unistr
+ self.safestr += self.unistr
+ self.safestr += self.bindata
+ self.safestr += self.test_object
+ self.safestr += self.test_object2
+
+ self.assertIn(self.asciistr, self.safestr)
+
+ if PY == 2:
+ self.assertIn("OMITTED OBJECT WITHOUT __str__ METHOD", self.safestr)
+ # should be included twice -- appended enc_unistr and unistr
+ self.assertIn(str(self.enc_unistr), self.safestr)
+ else:
+ self.assertIn("
+
+
+
+ False
+ center-always
+ 600
+ dialog
+ True
+
+
+
+ True
+ False
+ vertical
+
+
+ True
+ False
+ start
+ center
+ 20
+ 30
+
+
+ True
+ False
+ gtk-dialog-warning
+ 5
+
+
+ False
+ True
+ 7
+ 0
+
+
+
+
+ True
+ False
+ An unknown error has occurred
+
+
+
+
+
+
+ False
+ True
+ 1
+
+
+
+
+ False
+ False
+ 0
+
+
+
+
+ True
+ False
+ start
+ start
+ 40
+ 40
+ This program has encountered an unknown error. You may
+report the bug below or quit the program.
+
+
+ False
+ True
+ 21
+ 1
+
+
+
+
+ True
+ False
+ start
+ 20
+ 10
+ end
+
+
+ _Report Bug
+ True
+ True
+ True
+ True
+
+
+
+ False
+ True
+ 0
+
+
+
+
+ _Quit
+ True
+ True
+ True
+ True
+
+
+
+ False
+ True
+ 1
+
+
+
+
+ False
+ True
+ 2
+
+
+
+
+ True
+ True
+ 10
+ 10
+ 10
+ True
+
+
+
+ True
+ False
+ True
+ vertical
+ 6
+
+
+ True
+ False
+ start
+ 2
+ 0.10000000149011612
+ The output below may help determine the cause of the error:
+
+
+
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ True
+ True
+ in
+
+
+ True
+ True
+ natural
+ False
+ False
+ tracebackBuffer
+
+
+
+
+ True
+ True
+ 1
+
+
+
+
+ True
+ False
+ 6
+
+
+ True
+ False
+ 1
+ 'Debug' may take you to tty1.
+
+
+
+
+
+
+ True
+ True
+ 0
+
+
+
+
+ _Debug
+ True
+ True
+ True
+ True
+
+
+
+ False
+ True
+ 1
+
+
+
+
+ False
+ True
+ 2
+
+
+
+
+
+
+ True
+ False
+ More _info...
+ True
+
+
+
+
+ False
+ True
+ 3
+
+
+
+
+
+
+ No additional info available.
+
+
diff --git a/pkgbuilds/python-meh/src/python-meh-r0.43-1/zanata.xml b/pkgbuilds/python-meh/src/python-meh-r0.43-1/zanata.xml
new file mode 100644
index 0000000..a70990f
--- /dev/null
+++ b/pkgbuilds/python-meh/src/python-meh-r0.43-1/zanata.xml
@@ -0,0 +1,106 @@
+
+
+ https://fedora.zanata.org/
+ python-meh
+ master
+ gettext
+
+
+ sq
+ ar
+ as
+ ast
+ bal
+ eu
+ bn
+ bn-IN
+ brx
+ bs
+ br
+ bg
+ ca
+ zh-CN
+ zh-HK
+ zh-TW
+ kw
+ kw-GB
+ cs
+ da
+ nl
+ en-GB
+ eo
+ et
+ fi
+ fr
+ gl
+ ka
+ de
+ el
+ gu
+ he
+ hi
+ hu
+ is
+ id
+ ia
+ it
+ ja
+ kn
+ kk
+ km
+ ky
+ ko
+ lt
+ nds
+ mk
+ mai
+ ms
+ ml
+ mr
+ mn
+ ne
+ nb
+ nn
+ or
+ pa
+ fa
+ pl
+ pt
+ pt-BR
+ ro
+ ru
+ sr
+ sr@latin
+ si
+ sk
+ sl
+ es
+ sv
+ tg
+ ta
+ te
+ bo
+ tr
+ uk
+ ur
+ wba
+ cy
+ lv
+ kw@uccor
+ kw@kkcor
+ af
+ am
+ be
+ hr
+ de-CH
+ th
+ vi
+ zu
+ ilo
+ nso
+ tw
+ yo
+ anp
+
+
+
diff --git a/pkgbuilds/python-simpleline/PKGBUILD b/pkgbuilds/python-simpleline/PKGBUILD
new file mode 100644
index 0000000..144eace
--- /dev/null
+++ b/pkgbuilds/python-simpleline/PKGBUILD
@@ -0,0 +1,27 @@
+# Maintainer: FWS
+# Dépendance de l'installateur Anaconda (rhinstaller) — framework TUI texte.
+# Pur Python (setuptools find_packages, aucune extension C) : on installe
+# l'arborescence à la main pour éviter d'exiger python-build/python-installer.
+pkgname=python-simpleline
+pkgver=1.8
+pkgrel=1
+pkgdesc="Python3 text UI framework (dépendance Anaconda)"
+arch=('any')
+url="https://github.com/rhinstaller/python-simpleline"
+license=('LGPL-3.0-or-later')
+depends=('python' 'python-gobject')
+_tag="simpleline-$pkgver"
+source=("$pkgname-$pkgver.tar.gz::https://github.com/rhinstaller/python-simpleline/archive/refs/tags/$_tag.tar.gz")
+sha256sums=('b58cfb3ac4166b8bbbd1d3789713dc364c50509a513ead7be66a237a5b3affa3')
+_srcdir="python-simpleline-$_tag"
+
+package() {
+ cd "$srcdir/$_srcdir"
+ local sitedir
+ sitedir=$(python -c 'import site; print(site.getsitepackages()[0])')
+ install -d "$pkgdir$sitedir"
+ cp -a simpleline "$pkgdir$sitedir/simpleline"
+ # -d : grave le chemin d'exécution réel dans les .pyc (pas le $pkgdir de build).
+ python -m compileall -q -d "$sitedir/simpleline" "$pkgdir$sitedir/simpleline" || true
+ install -Dm644 LICENSE.md "$pkgdir/usr/share/licenses/$pkgname/LICENSE.md"
+}
diff --git a/pkgbuilds/python-simpleline/pkg/python-simpleline/.BUILDINFO b/pkgbuilds/python-simpleline/pkg/python-simpleline/.BUILDINFO
new file mode 100644
index 0000000..609eff9
--- /dev/null
+++ b/pkgbuilds/python-simpleline/pkg/python-simpleline/.BUILDINFO
@@ -0,0 +1,898 @@
+format = 2
+pkgname = python-simpleline
+pkgbase = python-simpleline
+pkgver = 1.8-1
+pkgarch = any
+pkgbuild_sha256sum = 7502d64c9ab7b8658cf85b6919f95cec8bbdc1febca98f91b01de326450080ea
+packager = Unknown Packager
+builddate = 1781307804
+builddir = /home/nocode/Files/FWS-ISO/pkgbuilds/python-simpleline
+startdir = /home/nocode/Files/FWS-ISO/pkgbuilds/python-simpleline
+buildtool = makepkg
+buildtoolver = 7.1.0
+buildenv = !distcc
+buildenv = color
+buildenv = !ccache
+buildenv = check
+buildenv = !sign
+options = strip
+options = docs
+options = !libtool
+options = !staticlibs
+options = emptydirs
+options = zipman
+options = purge
+options = debug
+options = lto
+installed = aardvark-dns-1.17.1-1-x86_64
+installed = abseil-cpp-20260107.1-1-x86_64
+installed = accountsservice-26.13.3-1-x86_64
+installed = acl-2.3.2-2-x86_64
+installed = adwaita-cursors-50.0-1-any
+installed = adwaita-fonts-50.0-1-any
+installed = adwaita-icon-theme-50.0-1-any
+installed = adwaita-icon-theme-legacy-46.2-3-any
+installed = alsa-card-profiles-1:1.6.6-1-x86_64
+installed = alsa-lib-1.2.16-1-x86_64
+installed = alsa-plugins-1:1.2.12-5-x86_64
+installed = alsa-topology-conf-1.2.5.1-4-any
+installed = alsa-ucm-conf-1.2.16-2-any
+installed = aom-3.14.1-1-x86_64
+installed = appstream-1.1.2-1-x86_64
+installed = aquamarine-0.12.1-1-x86_64
+installed = arch-install-scripts-31-1-any
+installed = archiso-88-1-any
+installed = archlinux-keyring-20260420-1-any
+installed = argon2-20190702-6-x86_64
+installed = at-spi2-core-2.60.4-1-x86_64
+installed = atkmm-2.28.5-2-x86_64
+installed = attr-2.5.2-2-x86_64
+installed = audit-4.1.4-2-x86_64
+installed = autoconf-2.73-1-any
+installed = automake-1.18.1-1-any
+installed = avahi-1:0.9rc4-1-x86_64
+installed = base-3-3-any
+installed = base-devel-1-2-any
+installed = bash-5.3.15-1-x86_64
+installed = bemoji-0.4.0-1-any
+installed = binutils-2.46+r70+g155188ea10a7-1-x86_64
+installed = bison-3.8.2-8-x86_64
+installed = blas-3.12.1-2-x86_64
+installed = bluez-libs-5.86-6-x86_64
+installed = boost-libs-1.91.0-1-x86_64
+installed = brightnessctl-0.5.1-3-x86_64
+installed = brotli-1.2.0-1-x86_64
+installed = bubblewrap-0.11.2-1-x86_64
+installed = bzip2-1.0.8-6-x86_64
+installed = c-ares-1.34.6-1-x86_64
+installed = ca-certificates-20240618-1-any
+installed = ca-certificates-mozilla-3.124-1-x86_64
+installed = ca-certificates-utils-20240618-1-any
+installed = cabextract-1.11-2-x86_64
+installed = cairo-1.18.4-1-x86_64
+installed = cairomm-1.14.6-1-x86_64
+installed = cairomm-1.16-1.18.1-1-x86_64
+installed = catatonit-0.2.1-2-x86_64
+installed = cblas-3.12.1-2-x86_64
+installed = cliphist-1:0.7.0-2-x86_64
+installed = clucene-2.3.3.4-17-x86_64
+installed = cmake-4.3.3-1-x86_64
+installed = compiler-rt-22.1.6-1-x86_64
+installed = composefs-1.0.8-1-x86_64
+installed = conmon-1:2.2.1-1-x86_64
+installed = containers-common-1:0.67.1-3-any
+installed = coreutils-9.11-1-x86_64
+installed = cpio-2.15-3-x86_64
+installed = cppdap-1.58.0-3-x86_64
+installed = cracklib-2.10.3-1-x86_64
+installed = criu-4.2-3-x86_64
+installed = crun-1.28-1-x86_64
+installed = cryptsetup-2.8.6-1-x86_64
+installed = curl-8.20.0-7-x86_64
+installed = dav1d-1.5.3-1-x86_64
+installed = db5.3-5.3.28-7-x86_64
+installed = dbus-1.16.2-1-x86_64
+installed = dbus-broker-37-3-x86_64
+installed = dbus-broker-units-37-3-x86_64
+installed = dbus-units-37-3-x86_64
+installed = dconf-0.49.0-1-x86_64
+installed = debugedit-5.3-1-x86_64
+installed = default-cursors-3-1-any
+installed = desktop-file-utils-0.28-1-x86_64
+installed = device-mapper-2.03.41-1-x86_64
+installed = diffutils-3.12-2-x86_64
+installed = discord-1:1.0.142-1-x86_64
+installed = dkms-3.4.1-1-any
+installed = dos2unix-7.5.6-1-x86_64
+installed = dosfstools-4.2-5-x86_64
+installed = double-conversion-3.4.0-1-x86_64
+installed = duktape-2.7.0-7-x86_64
+installed = e2fsprogs-1.47.4-1-x86_64
+installed = efibootmgr-18-4-x86_64
+installed = efivar-39-2-x86_64
+installed = egl-gbm-1.1.3-1-x86_64
+installed = egl-wayland-4:1.1.21-1-x86_64
+installed = egl-wayland2-1.0.1-1-x86_64
+installed = egl-x11-1.0.5-1-x86_64
+installed = eglexternalplatform-1.2.1-1-any
+installed = electron41-41.7.2-1-x86_64
+installed = elfutils-0.195-1-x86_64
+installed = erofs-utils-1.9.1-1-x86_64
+installed = exo-4.20.0-2-x86_64
+installed = expat-2.8.1-1-x86_64
+installed = extra-cmake-modules-6.26.0-1-any
+installed = fakeroot-1:1.37.2-1-x86_64
+installed = fcft-3.3.3-1-x86_64
+installed = ffmpeg-2:8.1.1-2-x86_64
+installed = fftw-3.3.11-1-x86_64
+installed = file-5.48-1-x86_64
+installed = filesystem-2025.10.12-1-any
+installed = findutils-4.10.0-3-x86_64
+installed = firefox-151.0.4-1-x86_64
+installed = flac-1.5.0-1-x86_64
+installed = flatpak-1:1.18.0-1-x86_64
+installed = flex-2.6.4-6-x86_64
+installed = fmt-12.1.0-2-x86_64
+installed = fontconfig-2:2.18.1-1-x86_64
+installed = freerdp-2:3.26.0-1-x86_64
+installed = freetype2-2.14.3-1-x86_64
+installed = fribidi-1.0.16-2-x86_64
+installed = fuse-common-3.18.2-1-x86_64
+installed = fuse2-2.9.9-5-x86_64
+installed = fuse3-3.18.2-1-x86_64
+installed = fuzzel-1.14.1-1-x86_64
+installed = gawk-5.4.0-1-x86_64
+installed = gc-8.2.12-1-x86_64
+installed = gcc-16.1.1+r12+g301eb08fa2c5-1-x86_64
+installed = gcc-libs-16.1.1+r12+g301eb08fa2c5-1-x86_64
+installed = gcr-3.41.2-2-x86_64
+installed = gcr-4-4.4.0.1-1-x86_64
+installed = gdb-17.2-1-x86_64
+installed = gdb-common-17.2-1-x86_64
+installed = gdbm-1.26-2-x86_64
+installed = gdk-pixbuf2-2.44.6-2-x86_64
+installed = gettext-1.0-2-x86_64
+installed = giflib-6.1.3-1-x86_64
+installed = git-2.54.0-1-x86_64
+installed = glib-networking-1:2.80.1-1-x86_64
+installed = glib2-2.88.1-1-x86_64
+installed = glibc-2.43+r22+g8362e8ce10b2-2-x86_64
+installed = glibmm-2.66.8-2-x86_64
+installed = glibmm-2.68-2.88.0-2-x86_64
+installed = glslang-1:1.4.350.0-1-x86_64
+installed = glycin-2.1.1-1-x86_64
+installed = gmp-6.3.0-3-x86_64
+installed = gnome-themes-extra-1:3.28-1-any
+installed = gnulib-l10n-20241231-1-any
+installed = gnupg-2.4.9-1-x86_64
+installed = gnutls-3.8.13-2-x86_64
+installed = go-2:1.26.4-1-x86_64
+installed = gobject-introspection-1.86.0-2-x86_64
+installed = gobject-introspection-runtime-1.86.0-2-x86_64
+installed = gparted-1.8.1-2-x86_64
+installed = gperftools-2.18.1-1-x86_64
+installed = gpgme-2.1.0-1-x86_64
+installed = gpgmepp-2.1.0-1-x86_64
+installed = gpm-1.20.7.r38.ge82d1a6-6-x86_64
+installed = gpsd-3.27.5-1-x86_64
+installed = gptfdisk-1.0.10-2-x86_64
+installed = graphene-1.10.8-2-x86_64
+installed = graphite-1:1.3.15-1-x86_64
+installed = grep-3.12-2-x86_64
+installed = grim-1.5.0-2-x86_64
+installed = groff-1.24.1-1-x86_64
+installed = grub-2:2.14-1-x86_64
+installed = gsettings-desktop-schemas-50.1-1-any
+installed = gsettings-system-schemas-50.1-1-any
+installed = gsm-1.0.24-1-x86_64
+installed = gssdp-1.6.5-1-x86_64
+installed = gst-plugins-bad-libs-1.28.3-1-x86_64
+installed = gst-plugins-base-libs-1.28.3-1-x86_64
+installed = gstreamer-1.28.3-1-x86_64
+installed = gtest-1.17.0-2-x86_64
+installed = gtk-layer-shell-0.10.1-1-x86_64
+installed = gtk-update-icon-cache-1:4.22.4-1-x86_64
+installed = gtk3-1:3.24.52-1-x86_64
+installed = gtk4-1:4.22.4-1-x86_64
+installed = gtkmm-4.0-4.22.0-2-x86_64
+installed = gtkmm3-3.24.11-1-x86_64
+installed = guile-3.0.11-1-x86_64
+installed = gupnp-1:1.6.10-1-x86_64
+installed = gupnp-igd-1.6.0-2-x86_64
+installed = gzip-1.14-2-x86_64
+installed = harfbuzz-14.2.1-1-x86_64
+installed = harfbuzz-icu-14.2.1-1-x86_64
+installed = hicolor-icon-theme-0.18-1-any
+installed = hidapi-0.15.0-1-x86_64
+installed = highway-1.4.0-1-x86_64
+installed = hunspell-1.7.3-1-x86_64
+installed = hwdata-0.408-1-any
+installed = hwloc-2.13.0-1-x86_64
+installed = hyphen-2.8.9-1-x86_64
+installed = hyprcursor-0.1.13-6-x86_64
+installed = hyprgraphics-0.5.1-2-x86_64
+installed = hypridle-0.1.7-9-x86_64
+installed = hyprland-0.55.4-1-x86_64
+installed = hyprland-guiutils-0.2.1-6-x86_64
+installed = hyprland-qt-support-0.1.0-12-x86_64
+installed = hyprlang-0.6.8-4-x86_64
+installed = hyprlock-0.9.5-4-x86_64
+installed = hyprpaper-0.8.4-2-x86_64
+installed = hyprpicker-0.4.7-1-x86_64
+installed = hyprpolkitagent-0.1.3-7-x86_64
+installed = hyprshot-1.3.0-4-any
+installed = hyprtoolkit-0.5.4-2-x86_64
+installed = hyprutils-0.13.1-1-x86_64
+installed = hyprwayland-scanner-0.4.6-1-x86_64
+installed = hyprwire-0.3.1-2-x86_64
+installed = iana-etc-20260530-1-any
+installed = icu-78.3-1-x86_64
+installed = imagemagick-7.1.2.25-1-x86_64
+installed = iniparser-4.2.6-2-x86_64
+installed = iproute2-7.0.0-1-x86_64
+installed = iptables-1:1.8.13-1-x86_64
+installed = iputils-20250605-1-x86_64
+installed = iso-codes-4.20.1-1-any
+installed = jack2-1.9.22-2-x86_64
+installed = jansson-2.15.0-1-x86_64
+installed = jbigkit-2.1-8-x86_64
+installed = jq-1.8.1-3-x86_64
+installed = json-c-0.18-2-x86_64
+installed = json-glib-1.10.8-1-x86_64
+installed = jsoncpp-1.9.6-3-x86_64
+installed = kbd-2.10.0-1-x86_64
+installed = kcoreaddons-6.26.0-1-x86_64
+installed = keyutils-1.6.3-4-x86_64
+installed = ki18n-6.26.0-1-x86_64
+installed = kitty-0.47.1-1-x86_64
+installed = kitty-shell-integration-0.47.1-1-x86_64
+installed = kitty-terminfo-0.47.1-1-x86_64
+installed = kmod-34.2-1-x86_64
+installed = kpmcore-26.04.2-1-x86_64
+installed = krb5-1.22.2-1-x86_64
+installed = kwidgetsaddons-6.26.0-1-x86_64
+installed = l-smash-2.14.5-4-x86_64
+installed = lame-3.101.r6531-1-x86_64
+installed = lapack-3.12.1-2-x86_64
+installed = lcms2-2.19.1-1-x86_64
+installed = leancrypto-1.7.2-1-x86_64
+installed = lib32-alsa-lib-1.2.16-1-x86_64
+installed = lib32-alsa-plugins-1.2.12-1-x86_64
+installed = lib32-brotli-1.1.0-1-x86_64
+installed = lib32-bzip2-1.0.8-4-x86_64
+installed = lib32-curl-8.20.0-7-x86_64
+installed = lib32-dbus-1.16.2-1-x86_64
+installed = lib32-e2fsprogs-1.47.4-1-x86_64
+installed = lib32-expat-2.8.1-1-x86_64
+installed = lib32-fontconfig-2:2.18.1-1-x86_64
+installed = lib32-freetype2-2.14.3-1-x86_64
+installed = lib32-gcc-libs-16.1.1+r12+g301eb08fa2c5-1-x86_64
+installed = lib32-glib2-2.88.1-1-x86_64
+installed = lib32-glibc-2.43+r22+g8362e8ce10b2-2-x86_64
+installed = lib32-gmp-6.3.0-2-x86_64
+installed = lib32-gnutls-3.8.13-3-x86_64
+installed = lib32-icu-78.3-1-x86_64
+installed = lib32-json-c-0.18-2-x86_64
+installed = lib32-keyutils-1.6.3-4-x86_64
+installed = lib32-krb5-1.22.2-1-x86_64
+installed = lib32-libdrm-2.4.134-1-x86_64
+installed = lib32-libelf-0.195-1-x86_64
+installed = lib32-libffi-3.5.2-1-x86_64
+installed = lib32-libgcrypt-1.12.2-1-x86_64
+installed = lib32-libglvnd-1.7.0-1-x86_64
+installed = lib32-libgpg-error-1.61-1-x86_64
+installed = lib32-libidn2-2.3.8-1-x86_64
+installed = lib32-libldap-2.6.13-1-x86_64
+installed = lib32-libnghttp2-1.69.0-1-x86_64
+installed = lib32-libnghttp3-1.16.0-1-x86_64
+installed = lib32-libngtcp2-1.23.0-1-x86_64
+installed = lib32-libnm-1.56.1-1-x86_64
+installed = lib32-libpciaccess-0.19-1-x86_64
+installed = lib32-libpipewire-1:1.6.6-1-x86_64
+installed = lib32-libpng-1.6.58-1-x86_64
+installed = lib32-libpsl-0.21.5-1-x86_64
+installed = lib32-libssh2-1.11.1-1-x86_64
+installed = lib32-libtasn1-4.21.0-1-x86_64
+installed = lib32-libunistring-1.4.2-1-x86_64
+installed = lib32-libva-2.22.0-1-x86_64
+installed = lib32-libx11-1.8.13-1-x86_64
+installed = lib32-libxau-1.0.12-1-x86_64
+installed = lib32-libxcb-1.17.0-1-x86_64
+installed = lib32-libxcrypt-4.5.2-1-x86_64
+installed = lib32-libxcrypt-compat-4.5.2-1-x86_64
+installed = lib32-libxdmcp-1.1.5-1-x86_64
+installed = lib32-libxext-1.3.7-1-x86_64
+installed = lib32-libxfixes-6.0.1-2-x86_64
+installed = lib32-libxinerama-1.1.6-1-x86_64
+installed = lib32-libxml2-2.15.3-1-x86_64
+installed = lib32-libxshmfence-1.3.3-1-x86_64
+installed = lib32-libxss-1.2.5-1-x86_64
+installed = lib32-libxxf86vm-1.1.5-2-x86_64
+installed = lib32-llvm-libs-1:22.1.6-1-x86_64
+installed = lib32-lm_sensors-1:3.6.2-2-x86_64
+installed = lib32-mesa-1:26.1.2-1-x86_64
+installed = lib32-ncurses-6.6-2-x86_64
+installed = lib32-nettle-4.0-2-x86_64
+installed = lib32-nspr-4.39-1-x86_64
+installed = lib32-nss-3.124-1-x86_64
+installed = lib32-nvidia-utils-610.43.02-1-x86_64
+installed = lib32-openssl-1:3.6.3-1-x86_64
+installed = lib32-p11-kit-0.26.2-1-x86_64
+installed = lib32-pcre2-10.47-1-x86_64
+installed = lib32-pipewire-1:1.6.6-1-x86_64
+installed = lib32-spirv-tools-1:1.4.350.0-1-x86_64
+installed = lib32-sqlite-3.53.2-1-x86_64
+installed = lib32-systemd-260.2-1-x86_64
+installed = lib32-util-linux-2.42.1-1-x86_64
+installed = lib32-vulkan-icd-loader-1.4.350.0-1-x86_64
+installed = lib32-wayland-1.25.0-1-x86_64
+installed = lib32-xz-5.8.3-1-x86_64
+installed = lib32-zlib-1.3.2-1-x86_64
+installed = lib32-zstd-1.5.7-2-x86_64
+installed = libabw-0.1.4-1-x86_64
+installed = libadwaita-1:1.9.1-1-x86_64
+installed = libaio-0.3.113-4-x86_64
+installed = libappindicator-12.10.1-1-x86_64
+installed = libarchive-3.8.7-1-x86_64
+installed = libasan-16.1.1+r12+g301eb08fa2c5-1-x86_64
+installed = libass-0.17.4-2-x86_64
+installed = libassuan-3.0.0-1-x86_64
+installed = libasyncns-1:0.8+r3+g68cd5af-3-x86_64
+installed = libatomic-16.1.1+r12+g301eb08fa2c5-1-x86_64
+installed = libatomic_ops-7.8.2-2-x86_64
+installed = libavc1394-0.5.4-7-x86_64
+installed = libavif-1.4.2-1-x86_64
+installed = libb2-0.98.1-3-x86_64
+installed = libblake3-1.8.4-1-x86_64
+installed = libbluray-1.4.1-1-x86_64
+installed = libbpf-1.7.0-1-x86_64
+installed = libbs2b-3.1.0-10-x86_64
+installed = libbsd-0.12.2-2-x86_64
+installed = libburn-1.5.8-1-x86_64
+installed = libcanberra-1:0.30+r2+gc0620e4-6-x86_64
+installed = libcap-2.78-1-x86_64
+installed = libcap-ng-0.9.3-1-x86_64
+installed = libcbor-0.14.0-1-x86_64
+installed = libcdio-2.3.0-1-x86_64
+installed = libcdio-paranoia-10.2+2.0.2-2-x86_64
+installed = libcdr-0.1.9-1-x86_64
+installed = libcloudproviders-0.4.0-1-x86_64
+installed = libcmis-0.6.3-1-x86_64
+installed = libcolord-1.4.8-1-x86_64
+installed = libcups-2:2.4.19-1-x86_64
+installed = libdaemon-0.14-6-x86_64
+installed = libdatrie-0.2.14-1-x86_64
+installed = libdbusmenu-glib-18.10.20180917-1-x86_64
+installed = libdbusmenu-gtk3-18.10.20180917-1-x86_64
+installed = libdecor-0.2.5-1-x86_64
+installed = libdeflate-1.25-1-x86_64
+installed = libdisplay-info-0.3.0-1-x86_64
+installed = libdovi-3.3.2-1-x86_64
+installed = libdrm-2.4.134-1-x86_64
+installed = libdvdnav-7.0.0-1-x86_64
+installed = libdvdread-7.0.1-1-x86_64
+installed = libe-book-0.1.3-20-x86_64
+installed = libebur128-1.2.6-2-x86_64
+installed = libedit-20260512_3.1-1-x86_64
+installed = libei-1.6.0-1-x86_64
+installed = libelf-0.195-1-x86_64
+installed = libepoxy-1.5.10-3-x86_64
+installed = libepubgen-0.1.1-6-x86_64
+installed = libetonyek-0.1.13-2-x86_64
+installed = libevdev-1.13.6-1-x86_64
+installed = libevent-2.1.12-5-x86_64
+installed = libexif-0.6.26-1-x86_64
+installed = libexttextcat-3.4.8-1-x86_64
+installed = libfdk-aac-2.0.3-2-x86_64
+installed = libffi-3.5.2-1-x86_64
+installed = libfido2-1.17.0-1-x86_64
+installed = libfontenc-1.1.9-1-x86_64
+installed = libfreeaptx-0.2.2-1-x86_64
+installed = libfreehand-0.1.3-1-x86_64
+installed = libfyaml-0.9.6-2-x86_64
+installed = libgcc-16.1.1+r12+g301eb08fa2c5-1-x86_64
+installed = libgcrypt-1.12.2-1-x86_64
+installed = libgfortran-16.1.1+r12+g301eb08fa2c5-1-x86_64
+installed = libgirepository-1.86.0-2-x86_64
+installed = libgit2-1:1.9.4-1-x86_64
+installed = libglvnd-1.7.0-3-x86_64
+installed = libgomp-16.1.1+r12+g301eb08fa2c5-1-x86_64
+installed = libgpg-error-1.61-1-x86_64
+installed = libgtop-2.41.3-2-x86_64
+installed = libgudev-238-3-x86_64
+installed = libice-1.1.2-1-x86_64
+installed = libidn2-2.3.8-1-x86_64
+installed = libiec61883-1.2.0-9-x86_64
+installed = libimobiledevice-1.4.0-2-x86_64
+installed = libimobiledevice-glue-1.3.2-1-x86_64
+installed = libinih-62-2-x86_64
+installed = libinput-1.31.3-1-x86_64
+installed = libisl-0.27-1-x86_64
+installed = libisoburn-1.5.8.2-1-x86_64
+installed = libisofs-1.5.8.2-1-x86_64
+installed = libixion-0.20.0-7-x86_64
+installed = libjpeg-turbo-3.1.4.1-1-x86_64
+installed = libjxl-0.11.2-2-x86_64
+installed = libksba-1.8.0-1-x86_64
+installed = liblangtag-0.6.8-1-x86_64
+installed = liblc3-1.1.3-2-x86_64
+installed = libldac-2.0.2.3-3-x86_64
+installed = libldap-2.6.13-1-x86_64
+installed = liblqr-0.4.3-1-x86_64
+installed = liblsan-16.1.1+r12+g301eb08fa2c5-1-x86_64
+installed = libmakepkg-dropins-20-1-any
+installed = libmalcontent-0.14.0-4-x86_64
+installed = libmd-1.2.0-1-x86_64
+installed = libmm-glib-1.24.2-1-x86_64
+installed = libmnl-1.0.5-2-x86_64
+installed = libmodplug-0.8.9.0-7-x86_64
+installed = libmpc-1.4.1-1-x86_64
+installed = libmpdclient-2.23-1-x86_64
+installed = libmspub-0.1.5-1-x86_64
+installed = libmwaw-0.3.22-4-x86_64
+installed = libmysofa-1.3.4-1-x86_64
+installed = libndp-1.9-1-x86_64
+installed = libnet-2:1.3-2-x86_64
+installed = libnetfilter_conntrack-1.1.1-1-x86_64
+installed = libnewt-0.52.25-2-x86_64
+installed = libnfnetlink-1.0.2-2-x86_64
+installed = libnftnl-1.3.1-1-x86_64
+installed = libnghttp2-1.69.0-1-x86_64
+installed = libnghttp3-1.16.0-1-x86_64
+installed = libngtcp2-1.23.0-1-x86_64
+installed = libnice-0.1.23-1-x86_64
+installed = libnl-3.12.0-1-x86_64
+installed = libnm-1.56.1-1-x86_64
+installed = libnma-1.10.6-3-x86_64
+installed = libnma-common-1.10.6-3-x86_64
+installed = libnotify-0.8.8-1-x86_64
+installed = libnsl-2.0.1-2-x86_64
+installed = libnumbertext-1.0.11-3-x86_64
+installed = libobjc-16.1.1+r12+g301eb08fa2c5-1-x86_64
+installed = libodfgen-0.1.8-5-x86_64
+installed = libogg-1.3.6-1-x86_64
+installed = libopenmpt-0.8.7-1-x86_64
+installed = libopenrazer-0.4.0-1-x86_64
+installed = liborcus-0.21.0-6-x86_64
+installed = libp11-kit-0.26.2-1-x86_64
+installed = libpagemaker-0.0.4-5-x86_64
+installed = libpcap-1.10.6-1-x86_64
+installed = libpciaccess-0.19-1-x86_64
+installed = libpgm-5.3.128-4-x86_64
+installed = libpipewire-1:1.6.6-1-x86_64
+installed = libplacebo-7.360.1-2-x86_64
+installed = libplist-2.7.0-3-x86_64
+installed = libpng-1.6.58-1-x86_64
+installed = libproxy-0.5.12-1-x86_64
+installed = libpsl-0.21.5-2-x86_64
+installed = libpulse-17.0+r98+gb096704c0-1-x86_64
+installed = libpwquality-1.4.5-7-x86_64
+installed = libquadmath-16.1.1+r12+g301eb08fa2c5-1-x86_64
+installed = libqxp-0.0.3-1-x86_64
+installed = libraqm-0.10.5-1-x86_64
+installed = libraw1394-2.1.2-4-x86_64
+installed = libreoffice-fresh-26.2.4-1-x86_64
+installed = librevenge-0.0.5-4-x86_64
+installed = librsvg-2:2.62.3-1-x86_64
+installed = librsync-1:2.3.4-2-x86_64
+installed = libsamplerate-0.2.2-3-x86_64
+installed = libsasl-2.1.28-5-x86_64
+installed = libseccomp-2.6.0-1-x86_64
+installed = libsecret-0.21.7-1-x86_64
+installed = libsigc++-2.12.2-1-x86_64
+installed = libsigc++-3.0-3.8.1-1-x86_64
+installed = libsixel-1.10.5-1-x86_64
+installed = libsm-1.2.6-1-x86_64
+installed = libsndfile-1.2.2-4-x86_64
+installed = libsodium-1.0.22-1-x86_64
+installed = libsoup3-3.6.6-2-x86_64
+installed = libsoxr-0.1.3-4-x86_64
+installed = libssh-0.12.0-1-x86_64
+installed = libssh2-1.11.1-1-x86_64
+installed = libstaroffice-0.0.7-5-x86_64
+installed = libstdc++-16.1.1+r12+g301eb08fa2c5-1-x86_64
+installed = libstemmer-3.1.1-1-x86_64
+installed = libsysprof-capture-50.0-2-x86_64
+installed = libtasn1-4.21.0-1-x86_64
+installed = libtatsu-1.0.5-1-x86_64
+installed = libteam-1.32-3-x86_64
+installed = libthai-0.1.30-1-x86_64
+installed = libtheora-1.2.0-1-x86_64
+installed = libtiff-4.7.1-2-x86_64
+installed = libtirpc-1.3.7-1-x86_64
+installed = libtommath-1.3.0-2-x86_64
+installed = libtool-2.6.1-1-x86_64
+installed = libtsan-16.1.1+r12+g301eb08fa2c5-1-x86_64
+installed = libubsan-16.1.1+r12+g301eb08fa2c5-1-x86_64
+installed = libunibreak-7.0-1-x86_64
+installed = libunistring-1.4.2-1-x86_64
+installed = libunwind-1.8.2-1-x86_64
+installed = liburing-2.14-1-x86_64
+installed = libusb-1.0.30-1-x86_64
+installed = libusbmuxd-2.1.1-2-x86_64
+installed = libutf8proc-2.11.3-1-x86_64
+installed = libuv-1.52.1-1-x86_64
+installed = libva-2.23.0-1-x86_64
+installed = libva-nvidia-driver-0.0.17-1-x86_64
+installed = libvdpau-1.5-4-x86_64
+installed = libverto-0.3.2-6-x86_64
+installed = libvisio-0.1.11-1-x86_64
+installed = libvorbis-1.3.7-4-x86_64
+installed = libvpl-2.16.0-2-x86_64
+installed = libvpx-1.16.0-3-x86_64
+installed = libwacom-2.19.0-1-x86_64
+installed = libwebp-1.6.0-2-x86_64
+installed = libwireplumber-0.5.14-1-x86_64
+installed = libwpd-0.10.3-6-x86_64
+installed = libwps-0.4.14-4-x86_64
+installed = libx11-1.8.13-1-x86_64
+installed = libxau-1.0.12-1-x86_64
+installed = libxcb-1.17.0-1-x86_64
+installed = libxcomposite-0.4.7-1-x86_64
+installed = libxcrypt-4.5.2-1-x86_64
+installed = libxcrypt-compat-4.5.2-1-x86_64
+installed = libxcursor-1.2.3-1-x86_64
+installed = libxcvt-0.1.3-1-x86_64
+installed = libxdamage-1.1.7-1-x86_64
+installed = libxdmcp-1.1.5-2-x86_64
+installed = libxext-1.3.7-1-x86_64
+installed = libxfce4ui-4.20.2-1-x86_64
+installed = libxfce4util-4.20.1-1-x86_64
+installed = libxfixes-6.0.2-1-x86_64
+installed = libxfont2-2.0.7-1-x86_64
+installed = libxft-2.3.9-1-x86_64
+installed = libxi-1.8.3-1-x86_64
+installed = libxinerama-1.1.6-1-x86_64
+installed = libxkbcommon-1.13.2-1-x86_64
+installed = libxkbcommon-x11-1.13.2-1-x86_64
+installed = libxkbfile-1.2.0-1-x86_64
+installed = libxml2-2.15.3-1-x86_64
+installed = libxml2-legacy-2.13.9-2-x86_64
+installed = libxmlb-0.3.27-1-x86_64
+installed = libxmp-4.7.0-1-x86_64
+installed = libxmu-1.3.1-1-x86_64
+installed = libxpresent-1.0.2-1-x86_64
+installed = libxrandr-1.5.5-1-x86_64
+installed = libxrender-0.9.12-1-x86_64
+installed = libxshmfence-1.3.3-1-x86_64
+installed = libxslt-1.1.45-2-x86_64
+installed = libxss-1.2.5-1-x86_64
+installed = libxt-1.3.1-1-x86_64
+installed = libxtst-1.2.5-1-x86_64
+installed = libxv-1.0.13-1-x86_64
+installed = libxxf86vm-1.1.7-1-x86_64
+installed = libyaml-0.2.5-3-x86_64
+installed = libyuv-r2426+464c51a03-1-x86_64
+installed = libzip-1.11.4-1-x86_64
+installed = libzmf-0.0.2-20-x86_64
+installed = licenses-20240728-1-any
+installed = lilv-0.26.4-1-x86_64
+installed = linux-7.0.11.arch1-1-x86_64
+installed = linux-api-headers-7.0-1-x86_64
+installed = linux-firmware-20260519-1-any
+installed = linux-firmware-amdgpu-20260519-1-any
+installed = linux-firmware-atheros-20260519-1-any
+installed = linux-firmware-broadcom-20260519-1-any
+installed = linux-firmware-cirrus-20260519-1-any
+installed = linux-firmware-intel-20260519-1-any
+installed = linux-firmware-mediatek-20260519-1-any
+installed = linux-firmware-nvidia-20260519-1-any
+installed = linux-firmware-other-20260519-1-any
+installed = linux-firmware-radeon-20260519-1-any
+installed = linux-firmware-realtek-20260519-1-any
+installed = linux-firmware-whence-20260519-1-any
+installed = linux-headers-7.0.11.arch1-1-x86_64
+installed = lld-22.1.6-1-x86_64
+installed = llhttp-9.3.1-1-x86_64
+installed = llvm-libs-22.1.6-1-x86_64
+installed = lm_sensors-1:3.6.2-1-x86_64
+installed = lmdb-0.9.35-1-x86_64
+installed = lpsolve-5.5.2.14-1-x86_64
+installed = lsb-release-2.0.r55.a25a4fc-1-any
+installed = lsof-4.99.6-1-x86_64
+installed = lua-5.5.0-2-x86_64
+installed = lua54-5.4.8-6-x86_64
+installed = luajit-2.1.1780076327+b925b3e-1-x86_64
+installed = lug-helper-1:4.13-1-any
+installed = lv2-1.18.10-2-x86_64
+installed = lz4-1:1.10.0-2-x86_64
+installed = lzo-2.10-5-x86_64
+installed = m4-1.4.21-2-x86_64
+installed = mailcap-2.1.54-2-any
+installed = make-4.4.1-3-x86_64
+installed = mako-1.11.0-1-x86_64
+installed = md4c-0.5.3-1-x86_64
+installed = mesa-1:26.1.2-1-x86_64
+installed = meson-1.11.1-3-any
+installed = mingw-w64-binutils-2.46.0-1-x86_64
+installed = mingw-w64-crt-14.0.0-1-any
+installed = mingw-w64-gcc-16.1.0-1-x86_64
+installed = mingw-w64-headers-14.0.0-1-any
+installed = mingw-w64-winpthreads-14.0.0-1-any
+installed = minizip-1:1.3.2-3-x86_64
+installed = mkinitcpio-41-4-any
+installed = mkinitcpio-busybox-1.36.1-1-x86_64
+installed = mobile-broadband-provider-info-20251101-1-any
+installed = mpdecimal-4.0.1-3-x86_64
+installed = mpfr-4.2.2-1-x86_64
+installed = mpg123-1.33.5-1-x86_64
+installed = mpv-1:0.41.0-3-x86_64
+installed = mpvpaper-1.8-1-x86_64
+installed = mtdev-1.1.7-1-x86_64
+installed = mtools-1:4.0.49-1-x86_64
+installed = mujs-1.3.9-1-x86_64
+installed = muparser-2.3.5-2-x86_64
+installed = nano-9.0-1-x86_64
+installed = ncurses-6.6-2-x86_64
+installed = neon-0.37.1-1-x86_64
+installed = netavark-1.17.2-2-x86_64
+installed = nettle-4.0-1-x86_64
+installed = network-manager-applet-1.36.0-2-x86_64
+installed = networkmanager-1.56.1-1-x86_64
+installed = nftables-1:1.1.6-3-x86_64
+installed = ninja-1.13.2-3-x86_64
+installed = nm-connection-editor-1.36.0-2-x86_64
+installed = noto-fonts-1:2026.06.01-1-any
+installed = noto-fonts-cjk-20240730-1-any
+installed = noto-fonts-emoji-1:2.051-1-any
+installed = noto-fonts-extra-1:2026.06.01-1-any
+installed = npth-1.8-1-x86_64
+installed = nspr-4.39-1-x86_64
+installed = nss-3.124-1-x86_64
+installed = nvidia-open-610.43.02-2-x86_64
+installed = nvidia-utils-610.43.02-2-x86_64
+installed = ocl-icd-2.3.4-1-x86_64
+installed = onetbb-2023.0.0-1-x86_64
+installed = oniguruma-6.9.10-1-x86_64
+installed = openal-1.25.2-1-x86_64
+installed = opencore-amr-0.1.6-2-x86_64
+installed = openjpeg2-2.5.4-1-x86_64
+installed = openrazer-daemon-3.12.2-2-any
+installed = openrazer-driver-dkms-3.12.2-2-any
+installed = openssh-10.3p1-1-x86_64
+installed = openssl-3.6.3-1-x86_64
+installed = opus-1.6.1-1-x86_64
+installed = opusfile-0.12-4-x86_64
+installed = orc-0.4.42-1-x86_64
+installed = os-prober-1.84-1-x86_64
+installed = ostree-2025.7-3-x86_64
+installed = p11-kit-0.26.2-1-x86_64
+installed = pacman-7.1.0.r9.g54d9411-2-x86_64
+installed = pacman-mirrorlist-20260610-1-any
+installed = pahole-1:1.31-2-x86_64
+installed = pam-1.7.2-2-x86_64
+installed = pambase-20250719-1-any
+installed = pango-1:1.57.1-1-x86_64
+installed = pangomm-2.46.4-3-x86_64
+installed = pangomm-2.48-2.56.1-2-x86_64
+installed = papirus-icon-theme-20250501-1-any
+installed = parted-3.7-1-x86_64
+installed = paru-2.1.0-2-x86_64
+installed = paru-debug-2.1.0-2-x86_64
+installed = passt-2026_05_26.038c51e-1-x86_64
+installed = patch-2.8-1-x86_64
+installed = pavucontrol-1:6.2-1-x86_64
+installed = pciutils-3.15.0-1-x86_64
+installed = pcre-8.45-4-x86_64
+installed = pcre2-10.47-1-x86_64
+installed = pcsclite-2.5.1-1-x86_64
+installed = perl-5.42.2-1-x86_64
+installed = perl-error-0.17030-3-any
+installed = perl-mailtools-2.22-3-any
+installed = perl-timedate-2.35-1-any
+installed = pinentry-1.3.2-2-x86_64
+installed = pipewire-1:1.6.6-1-x86_64
+installed = pipewire-audio-1:1.6.6-1-x86_64
+installed = pipewire-pulse-1:1.6.6-1-x86_64
+installed = pixman-0.46.4-1-x86_64
+installed = pkgconf-2.5.1-1-x86_64
+installed = playerctl-2.4.1-5-x86_64
+installed = podman-5.8.3-1-x86_64
+installed = podman-compose-1.6.0-1-any
+installed = podman-desktop-1.27.2-2-x86_64
+installed = polkit-127-3-x86_64
+installed = polkit-qt6-0.201.1-1-x86_64
+installed = poppler-26.05.0-1-x86_64
+installed = popt-1.19-2-x86_64
+installed = portaudio-1:19.7.0-4-x86_64
+installed = portmidi-1:2.0.8-1-x86_64
+installed = pps-tools-1.0.3-2-x86_64
+installed = procps-ng-4.0.6-1-x86_64
+installed = protobuf-35.0-1-x86_64
+installed = protobuf-c-1.5.2-10-x86_64
+installed = psmisc-23.7-2-x86_64
+installed = pugixml-1.15-3-x86_64
+installed = python-3.14.5-1-x86_64
+installed = python-attrs-26.1.0-1-any
+installed = python-autocommand-2.2.2-9-any
+installed = python-cffi-2.0.0-2-x86_64
+installed = python-click-8.3.3-1-any
+installed = python-cryptography-48.0.1-1-x86_64
+installed = python-daemonize-2.5.0-10-any
+installed = python-dbus-1.4.0-2-x86_64
+installed = python-dotenv-1.2.2-1-any
+installed = python-filelock-3.29.0-1-any
+installed = python-gobject-3.56.3-1-x86_64
+installed = python-jaraco.collections-5.1.0-3-any
+installed = python-jaraco.context-6.1.2-1-any
+installed = python-jaraco.functools-4.1.0-3-any
+installed = python-jaraco.text-4.0.0-4-any
+installed = python-mako-1.3.11-1-any
+installed = python-markdown-3.10.2-1-any
+installed = python-markupsafe-3.0.3-1-x86_64
+installed = python-more-itertools-11.1.0-1-any
+installed = python-numpy-2.4.6-1-x86_64
+installed = python-openrazer-3.12.2-2-any
+installed = python-packaging-26.2-1-any
+installed = python-pip-26.1.2-1-any
+installed = python-pkg_resources-81.0.0-1-any
+installed = python-platformdirs-4.10.0-1-any
+installed = python-protobuf-35.0-1-x86_64
+installed = python-pycparser-3.00-1-any
+installed = python-pygame-2.6.1-5-x86_64
+installed = python-pyudev-0.24.4-1-any
+installed = python-setproctitle-1.3.7-2-x86_64
+installed = python-setuptools-1:82.0.1-1-any
+installed = python-tqdm-4.68.1-1-any
+installed = python-typing_extensions-4.15.0-3-any
+installed = python-wheel-0.47.0-1-any
+installed = python-yaml-6.0.3-2-x86_64
+installed = qt5-base-5.15.19+kde+r96-1-x86_64
+installed = qt5-declarative-5.15.19+kde+r23-1-x86_64
+installed = qt5-graphicaleffects-5.15.19-1-x86_64
+installed = qt5-quickcontrols2-5.15.19+kde+r5-1-x86_64
+installed = qt5-svg-5.15.19+kde+r5-1-x86_64
+installed = qt5-translations-5.15.19-1-any
+installed = qt5-wayland-5.15.19+kde+r55-1-x86_64
+installed = qt6-5compat-6.11.1-1-x86_64
+installed = qt6-base-6.11.1-1-x86_64
+installed = qt6-declarative-6.11.1-3-x86_64
+installed = qt6-shadertools-6.11.1-1-x86_64
+installed = qt6-svg-6.11.1-1-x86_64
+installed = qt6-tools-6.11.1-1-x86_64
+installed = qt6-translations-6.11.1-1-any
+installed = qt6-wayland-6.11.1-1-x86_64
+installed = raptor-2.0.16-9-x86_64
+installed = rasqal-1:0.9.33-9-x86_64
+installed = rav1e-0.8.1-2-x86_64
+installed = razergenie-1.3.0-1-x86_64
+installed = re2-2:2025.11.05-4-x86_64
+installed = readline-8.3.003-1-x86_64
+installed = redland-1:1.0.17-10-x86_64
+installed = reflector-2023-5-any
+installed = resvg-0.47.0-2-x86_64
+installed = rhash-1.4.6-1-x86_64
+installed = rsync-3.4.4-1-x86_64
+installed = rtkit-0.14-1-x86_64
+installed = rubberband-4.0.0-2-x86_64
+installed = rustup-1.29.0-2-x86_64
+installed = sbc-2.2-1-x86_64
+installed = scdoc-1.11.4-1-x86_64
+installed = sdbus-cpp-2.3.1-1-x86_64
+installed = sddm-0.21.0-7-x86_64
+installed = sddm-catppuccin-git-r42.10831de-1-any
+installed = sdl2-compat-2.32.70-1-x86_64
+installed = sdl2_image-2.8.12-1-x86_64
+installed = sdl2_mixer-2.8.2-1-x86_64
+installed = sdl2_ttf-2.24.0-2-x86_64
+installed = sdl3-3.4.10-1-x86_64
+installed = sdl3_ttf-3.2.2-3-x86_64
+installed = seatd-0.9.3-1-x86_64
+installed = sed-4.10-1-x86_64
+installed = serd-0.32.10-1-x86_64
+installed = shaderc-2026.2-1-x86_64
+installed = shadow-4.18.0-1-x86_64
+installed = shared-mime-info-2.4-3-x86_64
+installed = slang-2.3.3-4-x86_64
+installed = slurp-1.5.0-2-x86_64
+installed = smartmontools-7.5-1-x86_64
+installed = snappy-1.2.2-3-x86_64
+installed = sndio-1.10.0-1-x86_64
+installed = sord-0.16.22-1-x86_64
+installed = sound-theme-freedesktop-0.8-6-any
+installed = source-highlight-3.1.9-18-x86_64
+installed = spandsp-0.0.6-7-x86_64
+installed = spdlog-1.17.0-2-x86_64
+installed = speex-1.2.1-2-x86_64
+installed = speexdsp-1.2.1-2-x86_64
+installed = spirv-tools-1:1.4.350.0-1-x86_64
+installed = sqlite-3.53.2-1-x86_64
+installed = squashfs-tools-4.7.5-1-x86_64
+installed = sratom-0.6.22-1-x86_64
+installed = srt-1.5.5-1-x86_64
+installed = startup-notification-0.12-9-x86_64
+installed = steam-1.0.0.85-7-x86_64
+installed = steam-devices-1.0.0.85-7-x86_64
+installed = sudo-1.9.17.p2-2-x86_64
+installed = svt-av1-4.1.0-1-x86_64
+installed = systemd-260.2-2-x86_64
+installed = systemd-libs-260.2-2-x86_64
+installed = systemd-sysvcompat-260.2-2-x86_64
+installed = tar-1.35-2-x86_64
+installed = tcl-8.6.16-1-x86_64
+installed = tdb-1.4.15-1-x86_64
+installed = texinfo-7.3-1-x86_64
+installed = thunar-4.20.8-3-x86_64
+installed = tinysparql-3.11.1-1-x86_64
+installed = tk-8.6.16-1-x86_64
+installed = tomlplusplus-3.4.0-2-x86_64
+installed = tpm2-tss-4.1.3-1-x86_64
+installed = tslib-1.24-1-x86_64
+installed = ttf-jetbrains-mono-nerd-3.4.0-2-any
+installed = tzdata-2026b-1-x86_64
+installed = uchardet-0.0.8-4-x86_64
+installed = uefitool-debug-1:0.28.0-3-x86_64
+installed = unzip-6.0-23-x86_64
+installed = upower-1.91.2-1-x86_64
+installed = usbutils-019-1-x86_64
+installed = util-linux-2.42.1-1-x86_64
+installed = util-linux-libs-2.42.1-1-x86_64
+installed = v4l-utils-1.32.0-2-x86_64
+installed = vapoursynth-76-1-x86_64
+installed = vid.stab-1.1.1-2-x86_64
+installed = vim-9.2.0623-1-x86_64
+installed = vim-runtime-9.2.0623-1-x86_64
+installed = vmaf-3.1.0-1-x86_64
+installed = vmware-keymaps-1.0-3-any
+installed = vmware-workstation-26H1-3-x86_64
+installed = vscodium-bin-1.121.03429-1-x86_64
+installed = vulkan-icd-loader-1.4.350.0-1-x86_64
+installed = vulkan-tools-1.4.350.0-1-x86_64
+installed = waybar-0.15.0-2-x86_64
+installed = wayland-1.25.0-1-x86_64
+installed = wayland-protocols-1.49-1-any
+installed = webrtc-audio-processing-1-1.3-5-x86_64
+installed = which-2.25-1-x86_64
+installed = winboat-bin-0.9.0-1-x86_64
+installed = wine-staging-11.10-1-x86_64
+installed = wireplumber-0.5.14-1-x86_64
+installed = wl-clipboard-1:2.3.0-1-x86_64
+installed = wlogout-1.2.2-0-x86_64
+installed = woff2-font-awesome-7.2.0-1-any
+installed = wpa_supplicant-2:2.11-5-x86_64
+installed = x264-3:0.165.r3222.b35605a-2-x86_64
+installed = x265-4.1-1-x86_64
+installed = xautomation-1.09-6-x86_64
+installed = xcb-proto-1.17.0-4-any
+installed = xcb-util-0.4.1-2-x86_64
+installed = xcb-util-cursor-0.1.6-1-x86_64
+installed = xcb-util-errors-1.0.1-2-x86_64
+installed = xcb-util-image-0.4.1-3-x86_64
+installed = xcb-util-keysyms-0.4.1-5-x86_64
+installed = xcb-util-renderutil-0.3.10-2-x86_64
+installed = xcb-util-wm-0.4.2-2-x86_64
+installed = xdg-dbus-proxy-0.1.7-1-x86_64
+installed = xdg-desktop-portal-1.22.0-1-x86_64
+installed = xdg-desktop-portal-gtk-1.15.3-1-x86_64
+installed = xdg-desktop-portal-hyprland-1.3.12-2-x86_64
+installed = xdg-user-dirs-0.20-1-x86_64
+installed = xdg-utils-1.2.1-2-any
+installed = xf86-input-libinput-1.5.0-1-x86_64
+installed = xfconf-4.20.0-2-x86_64
+installed = xkeyboard-config-2.47-1-any
+installed = xmlsec-1.3.11-1-x86_64
+installed = xorg-fonts-encodings-1.1.0-2-any
+installed = xorg-server-21.1.23-1-x86_64
+installed = xorg-server-common-21.1.23-1-x86_64
+installed = xorg-setxkbmap-1.3.5-1-x86_64
+installed = xorg-xauth-1.1.5-1-x86_64
+installed = xorg-xkbcomp-1.5.0-1-x86_64
+installed = xorg-xprop-1.2.8-1-x86_64
+installed = xorg-xrandr-1.5.4-1-x86_64
+installed = xorg-xwayland-24.1.12-1-x86_64
+installed = xorgproto-2025.1-1-any
+installed = xvidcore-1.3.7-3-x86_64
+installed = xxhash-0.8.3-1-x86_64
+installed = xz-5.8.3-1-x86_64
+installed = yaml-cpp-0.9.0-1-x86_64
+installed = yay-12.6.0-1-x86_64
+installed = yay-debug-12.6.0-1-x86_64
+installed = zenity-4.2.2-1-x86_64
+installed = zeromq-4.3.5-3-x86_64
+installed = zimg-3.0.6-1-x86_64
+installed = zint-2.16.0-2-x86_64
+installed = zix-0.8.0-1-x86_64
+installed = zlib-1:1.3.2-3-x86_64
+installed = zlib-ng-2.3.3-1-x86_64
+installed = zstd-1.5.7-3-x86_64
+installed = zxing-cpp-3.0.2-1-x86_64
diff --git a/pkgbuilds/python-simpleline/pkg/python-simpleline/.MTREE b/pkgbuilds/python-simpleline/pkg/python-simpleline/.MTREE
new file mode 100644
index 0000000..00916a2
Binary files /dev/null and b/pkgbuilds/python-simpleline/pkg/python-simpleline/.MTREE differ
diff --git a/pkgbuilds/python-simpleline/pkg/python-simpleline/.PKGINFO b/pkgbuilds/python-simpleline/pkg/python-simpleline/.PKGINFO
new file mode 100644
index 0000000..546682c
--- /dev/null
+++ b/pkgbuilds/python-simpleline/pkg/python-simpleline/.PKGINFO
@@ -0,0 +1,15 @@
+# Generated by makepkg 7.1.0
+# using fakeroot version 1.37.2
+pkgname = python-simpleline
+pkgbase = python-simpleline
+xdata = pkgtype=pkg
+pkgver = 1.8-1
+pkgdesc = Python3 text UI framework (dépendance Anaconda)
+url = https://github.com/rhinstaller/python-simpleline
+builddate = 1781307804
+packager = Unknown Packager
+size = 346552
+arch = any
+license = LGPL-3.0-or-later
+depend = python
+depend = python-gobject
diff --git a/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/__init__.py b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/__init__.py
new file mode 100644
index 0000000..1d4dfc7
--- /dev/null
+++ b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/__init__.py
@@ -0,0 +1,136 @@
+# Base class for Simpleline Text UI framework.
+#
+# Library containing the Text UI framework.
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+
+__all__ = ["App"]
+
+
+from simpleline.logging import setup_logging
+from simpleline.errors import NothingScheduledError
+
+setup_logging()
+
+
+class App():
+ """This is the main class for Simpleline library.
+
+ Do not create instance of this class. Use this class as static!
+ The `initialize()` method must be called before use.
+
+ It is giving you access to the scheduler and event loop. You can have only one instance of this
+ class in your application.
+
+ To create this instance call `App.initialize()` method. This method can also be used to
+ reset settings in the App class to start with new event loop or scheduler.
+ """
+ __app = None
+
+ class AppPimpl():
+
+ def __init__(self, scheduler, event_loop, configuration):
+ self.event_loop = event_loop
+ self.scheduler = scheduler
+ self.configuration = configuration
+
+ @classmethod
+ def initialize(cls, scheduler=None, event_loop=None, global_configuration=None):
+ """Create app instance inside of this class.
+
+ This method can be called multiple times to reset App settings.
+
+ :param scheduler: scheduler used for rendering screens; if not specified use
+ `simpleline.render.screen_scheduler.ScreenScheduler`.
+ :type scheduler: instance of `simpleline.render.screen_scheduler.ScreenScheduler`.
+
+ :param event_loop: event loop used for asynchronous tasks;
+ if not specified use `simpleline.event_loop.main_loop.MainLoop`.
+ :type event_loop: object based on class `simpleline.event_loop.AbstractEventLoop`.
+
+ :param global_configuration: instance of the global configuration object; if not specified
+ use `simpleline.global_configuration.GlobalConfiguration`
+ :type global_configuration: object based on class
+ `simpleline.global_configuration.GlobalConfiguration`
+ """
+ from simpleline.event_loop.main_loop import MainLoop # pylint: disable=import-outside-toplevel
+ from simpleline.render.screen_scheduler import ScreenScheduler # pylint: disable=import-outside-toplevel
+ from simpleline.global_configuration import GlobalConfiguration # pylint: disable=import-outside-toplevel
+
+ if event_loop is None:
+ event_loop = MainLoop()
+ if scheduler is None:
+ scheduler = ScreenScheduler(event_loop)
+ if global_configuration is None:
+ global_configuration = GlobalConfiguration()
+
+ cls.__app = cls.AppPimpl(scheduler, event_loop, global_configuration)
+
+ cls._post_init()
+
+ @classmethod
+ def _post_init(cls):
+ from simpleline.input.input_threading import InputThreadManager # pylint: disable=import-outside-toplevel
+ # FIXME: This should be done by more general way not by calling exact class here.
+ # Create new instance of InputThreadManager because it needs new event loop
+ InputThreadManager.create_new_instance()
+
+ @classmethod
+ def is_initialized(cls):
+ """Is the App already initialized?
+
+ :returns: True if the `App.initialized()` method was called, False otherwise.
+ """
+ if cls.__app is None:
+ return False
+
+ return True
+
+ @classmethod
+ def get_scheduler(cls):
+ """Get instance of class responsible for rendering of the screen."""
+ return cls.__app.scheduler
+
+ @classmethod
+ def get_event_loop(cls):
+ """Get instance of class responsible for processing asynchronous events."""
+ return cls.__app.event_loop
+
+ @classmethod
+ def get_configuration(cls):
+ """Get application defaults configuration object."""
+ return cls.__app.configuration
+
+ @classmethod
+ def run(cls):
+ """Run event loop.
+
+ Raise an exception if no screen is scheduled. This behavior can be changed by
+ `should_run_with_empty_stack` global configuration option.
+
+ This is shortcut to `App.event_loop().run()`.
+ :raises NothingScheduledError: when there is no screen scheduled
+ """
+ if not cls.__app.configuration.should_run_with_empty_stack:
+ # Check if the screen stack is not empty
+ if cls.__app.scheduler.nothing_to_render:
+ raise NothingScheduledError("Can't run application with the empty screen stack! "
+ "To avoid this please see should_run_with_empty_stack "
+ "global configuration option.")
+ App.get_event_loop().run()
diff --git a/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/__pycache__/__init__.cpython-314.pyc b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/__pycache__/__init__.cpython-314.pyc
new file mode 100644
index 0000000..a2f4f6f
Binary files /dev/null and b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/__pycache__/__init__.cpython-314.pyc differ
diff --git a/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/__pycache__/errors.cpython-314.pyc b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/__pycache__/errors.cpython-314.pyc
new file mode 100644
index 0000000..0a659ee
Binary files /dev/null and b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/__pycache__/errors.cpython-314.pyc differ
diff --git a/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/__pycache__/global_configuration.cpython-314.pyc b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/__pycache__/global_configuration.cpython-314.pyc
new file mode 100644
index 0000000..dbc584a
Binary files /dev/null and b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/__pycache__/global_configuration.cpython-314.pyc differ
diff --git a/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/__pycache__/logging.cpython-314.pyc b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/__pycache__/logging.cpython-314.pyc
new file mode 100644
index 0000000..5624786
Binary files /dev/null and b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/__pycache__/logging.cpython-314.pyc differ
diff --git a/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/errors.py b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/errors.py
new file mode 100644
index 0000000..4532c61
--- /dev/null
+++ b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/errors.py
@@ -0,0 +1,31 @@
+# Base exceptions for the Simpleline application.
+#
+# Base class for Simpleline Text UI framework.
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+# This can't be moved to __init__.py because of cyclic imports error.
+#
+
+
+class SimplelineError(Exception):
+ """Base exception for all other exceptions."""
+
+
+class NothingScheduledError(SimplelineError):
+ """Exception when running the loop with no screens scheduled."""
diff --git a/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/event_loop/__init__.py b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/event_loop/__init__.py
new file mode 100644
index 0000000..1b68307
--- /dev/null
+++ b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/event_loop/__init__.py
@@ -0,0 +1,264 @@
+# Abstract base class for Simpleline Event Loop.
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+
+# This class can be overridden to use any existing event loop of your program.
+#
+# Author(s): Jiri Konecny
+#
+
+import sys
+
+from abc import ABCMeta, abstractmethod
+from collections import namedtuple
+
+from simpleline.errors import SimplelineError
+from simpleline.event_loop.ticket_machine import TicketMachine
+from simpleline.logging import get_simpleline_logger
+
+log = get_simpleline_logger()
+
+__all__ = ["AbstractEventLoop", "AbstractSignal", "ExitMainLoop"]
+
+QuitCallback = namedtuple("QuitCallback", ["callback", "args"])
+
+
+class ExitMainLoop(SimplelineError):
+ """This exception ends the whole event loop."""
+
+
+class AbstractEventLoop(metaclass=ABCMeta):
+
+ def __init__(self):
+ super().__init__()
+ self._handlers = {}
+ self._processed_signals = TicketMachine()
+ self._quit_callback = None
+ # end most inner loop politely by setting to False
+ self._run_loop = True
+ self._force_quit = False
+
+ def register_signal_handler(self, signal, callback, data=None):
+ """Register a callback which will be called when message "event"
+ is encountered during process_events.
+
+ The callback has to accept two arguments:
+ - the received message in the form of (type, [arguments])
+ - the data registered with the handler
+
+ :param signal: Signal class we want to react on.
+ :type signal: Class based on the simpleline.event_loop.AbstractSignal class.
+
+ :param callback: The callback function.
+ :type callback: func(event_message, data)
+
+ :param data: Optional data to pass to callback.
+ :type data: Anything.
+ """
+ if signal not in self._handlers:
+ self._handlers[signal] = []
+
+ event_handler = self._create_event_handler(callback, data)
+ self._handlers[signal].append(event_handler)
+
+ @abstractmethod
+ def register_signal_source(self, signal_source):
+ """Register source of signal for actual event queue.
+
+ :param signal_source: Source for future signals.
+ :type signal_source: `simpleline.render.ui_screen.UIScreen`
+ """
+
+ @abstractmethod
+ def enqueue_signal(self, signal):
+ """Enqueue new event for processing.
+
+ :param signal: Signal which you want to add to the event queue for processing.
+ :type signal: Instance based on AbstractEvent class.
+ """
+ log.debug("New signal %s enqueued with source %s",
+ signal,
+ signal.source.__class__.__name__)
+
+ @abstractmethod
+ def run(self):
+ """Starts the event loop."""
+ log.debug("Starting main loop")
+ self._force_quit = False
+
+ def force_quit(self):
+ """Force quit all running event loops.
+
+ Kill all loop including inner loops (modal window).
+ None of the Simpleline events will be processed anymore.
+ """
+ log.debug("Force quit called. Killing all loops!")
+ self._force_quit = True
+
+ @abstractmethod
+ def execute_new_loop(self, signal):
+ """Starts the new event loop and pass `signal` in it.
+
+ This is required for processing a modal screens.
+
+ :param signal: Signal passed to the new event loop.
+ :type signal: The `AbstractSignal` based class.
+ """
+ log.debug("Executing inner loop")
+
+ @abstractmethod
+ def close_loop(self):
+ """Close active event loop.
+
+ Close an event loop created by the `execute_new_loop()` method.
+ """
+ log.debug("Closing inner loop")
+
+ @abstractmethod
+ def process_signals(self, return_after=None):
+ """This method processes incoming async messages.
+
+ Process signals enqueued by the `self.enqueue_signal()` method. Call handlers
+ registered to the signals by the `self.register_signal_handler()` method.
+
+ When `return_after` is specified then wait to the point when this signal is processed.
+ NO warranty that this method will return immediately after the signal was processed!
+
+ Without `return_after` parameter this method will return after all queued signals
+ with the highest priority will be processed.
+
+ The method is NOT thread safe!
+
+ :param return_after: Wait on this signal to be processed.
+ :type return_after: Class of the signal.
+ """
+
+ def set_quit_callback(self, callback, args=None):
+ """Call this callback when event loop quits.
+
+ :param callback: Call this callback when event loops ends (application quit).
+ :type callback: Function with one parameter data `func(data)`.
+
+ :param args: Arguments passed to the quit callback.
+ :type args: Anything.
+ """
+ self._quit_callback = QuitCallback(callback, args)
+
+ def kill_app_with_traceback(self, exception_signal, data=None):
+ """Print exception and screen stack and kill the application.
+
+ :param exception_signal: ExceptionSignal encapsulating the original exception which
+ will be passed to the sys.excepthook method.
+ :type exception_signal: Instance of `simpleline.event_loop.signals.ExceptionSignal` class.
+
+ :param data: To be usable as signal handler.
+ :type data: Anything will be ignored.
+ """
+ log.debug("Unhandled error in handler raised:")
+ sys.excepthook(*exception_signal.exception_info)
+
+ from simpleline import App # pylint: disable=import-outside-toplevel
+ stack_dump = App.get_scheduler().dump_stack()
+ print("")
+ print(stack_dump)
+ log.error(stack_dump)
+
+ log.debug("Killing application!")
+ sys.exit(1)
+
+ @staticmethod
+ def _create_event_handler(callback, data):
+ """Create event handler data object and return it."""
+ return EventHandler(callback=callback, data=data)
+
+ def _register_wait_on_signal(self, wait_on_signal):
+ """Register process waiting on signal `wait_on_signal` and return id for later checking.
+
+ ID is returned which is then used in the `self._check_if_signal_processed()` method.
+
+ :param wait_on_signal: Signal we are waiting for.
+ :type wait_on_signal: Class based on `simpleline.event_loop.AbstractSignal`.
+ """
+ return self._processed_signals.take_ticket(wait_on_signal.__name__)
+
+ def _mark_signal_processed(self, signal):
+ """Mark that processes waiting on this signal that they are able to go.
+
+ :param signal: Signal which was processed.
+ :type signal: Class based on `simpleline.event_loop.AbstractSignal`.
+ """
+ self._processed_signals.mark_line_to_go(signal.__class__.__name__)
+
+ def _check_if_signal_processed(self, wait_on_signal, unique_id):
+ """Check if the signal was processed.
+
+ :param wait_on_signal: Signal the process is waiting for.
+ :type wait_on_signal: Class based on `simpleline.event_loop.AbstractSignal`.
+
+ :param unique_id: Unique id returned by the `self._register_wait_on_signal()` method.
+ :type unique_id: int
+ """
+ return self._processed_signals.check_ticket(wait_on_signal.__name__, unique_id)
+
+
+class EventHandler():
+ """Data class to save event handlers."""
+
+ def __init__(self, callback, data):
+ self.callback = callback
+ self.data = data
+
+
+class AbstractSignal(metaclass=ABCMeta):
+ """This class is base class for signals.
+
+ .. NOTE:
+ Ordering and equality is based on priority.
+ """
+
+ def __init__(self, source, priority=0):
+ self._source = source
+ self._priority = priority
+
+ def __lt__(self, other):
+ """Order Signal classes by priority."""
+ return self._priority < other.priority
+
+ def __eq__(self, other):
+ """Order Signal classes by priority."""
+ return self._priority == other.priority
+
+ def __str__(self):
+ """For easier logging."""
+ return self.__class__.__name__
+
+ @property
+ def priority(self):
+ """Priority of this event.
+
+ Values less than 0 denote higher priorities. Values greater than 0 denote lower priorities.
+ Events from high priority sources are always processed before events from lower priority
+ sources.
+ """
+ return self._priority
+
+ @property
+ def source(self):
+ """Source which emitted this event."""
+ return self._source
diff --git a/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/event_loop/__pycache__/__init__.cpython-314.pyc b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/event_loop/__pycache__/__init__.cpython-314.pyc
new file mode 100644
index 0000000..4fdf398
Binary files /dev/null and b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/event_loop/__pycache__/__init__.cpython-314.pyc differ
diff --git a/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/event_loop/__pycache__/event_queue.cpython-314.pyc b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/event_loop/__pycache__/event_queue.cpython-314.pyc
new file mode 100644
index 0000000..0ebe6c5
Binary files /dev/null and b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/event_loop/__pycache__/event_queue.cpython-314.pyc differ
diff --git a/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/event_loop/__pycache__/glib_event_loop.cpython-314.pyc b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/event_loop/__pycache__/glib_event_loop.cpython-314.pyc
new file mode 100644
index 0000000..5d1f3ef
Binary files /dev/null and b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/event_loop/__pycache__/glib_event_loop.cpython-314.pyc differ
diff --git a/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/event_loop/__pycache__/main_loop.cpython-314.pyc b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/event_loop/__pycache__/main_loop.cpython-314.pyc
new file mode 100644
index 0000000..7724862
Binary files /dev/null and b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/event_loop/__pycache__/main_loop.cpython-314.pyc differ
diff --git a/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/event_loop/__pycache__/signals.cpython-314.pyc b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/event_loop/__pycache__/signals.cpython-314.pyc
new file mode 100644
index 0000000..9e43edb
Binary files /dev/null and b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/event_loop/__pycache__/signals.cpython-314.pyc differ
diff --git a/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/event_loop/__pycache__/ticket_machine.cpython-314.pyc b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/event_loop/__pycache__/ticket_machine.cpython-314.pyc
new file mode 100644
index 0000000..5a17f6d
Binary files /dev/null and b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/event_loop/__pycache__/ticket_machine.cpython-314.pyc differ
diff --git a/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/event_loop/event_queue.py b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/event_loop/event_queue.py
new file mode 100644
index 0000000..c0351d3
--- /dev/null
+++ b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/event_loop/event_queue.py
@@ -0,0 +1,153 @@
+# Default event queue for Simpleline application.
+#
+# This class is thread safe.
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+# Author(s): Jiri Konecny
+#
+
+
+from queue import PriorityQueue
+from threading import Lock
+
+from simpleline.errors import SimplelineError
+
+
+class EventQueueError(SimplelineError):
+ """Main exception for `EventQueue` class.
+
+ Inherits from `simpleline.SimplelineError`.
+ """
+
+
+class EventQueue():
+ """Class for managing signal queue.
+
+ Responsibilities of this class are:
+ * sorting by priority of signals
+ * managing sources for this event queue
+ * enqueuing signals
+ """
+
+ def __init__(self):
+ self._queue = PriorityQueue()
+ self._contained_screens = set()
+ self._lock = Lock()
+
+ def empty(self):
+ """Return true if Queue is empty.
+
+ :return: True if empty, False otherwise.
+ """
+ return self._queue.empty()
+
+ def enqueue(self, signal):
+ """Enqueue signal to this queue.
+
+ :param signal: Signal which should be enqueued to this queue.
+ :type signal: Signal class based on `simpleline.event_loop.signals.AbstractSignal`.
+ """
+ self._queue.put(signal)
+
+ def enqueue_if_source_belongs(self, signal, source):
+ """Enqueue signal to this queue if the signal source belongs to this queue.
+
+ Enqueue the `signal` only if the `source` belongs to this queue.
+ See the `add_source()` method.
+
+ :param signal: Signal which should be enqueued to this queue.
+ :type signal: Signal class based on `simpleline.event_loop.signals.AbstractSignal`.
+ :param source: Source of this signal.
+ :type source: Anything.
+ :return: True if the source belongs to this queue and signal was queued, False otherwise.
+ :rtype: bool
+ """
+ if self.contains_source(source):
+ self._queue.put(signal)
+ return True
+
+ return False
+
+ def get(self):
+ """Return enqueued signal with the highest priority.
+
+ This is FIFO implementation for the same priority.
+ If the queue is empty this method will wait for the input signal.
+
+ :return: Queued signal.
+ :rtype: Signal based on class `simpleline.event_loop.signals.AbstractSignal`.
+ """
+ return self._queue.get()
+
+ def get_top_event_if_priority(self, priority):
+ """Return top enqueued signal if priority is equal to `priority`. Otherwise `None`.
+
+ :param priority: Requested event priority.
+ :type priority: int
+
+ :return: Queued signal if it has requested priority. Otherwise `None`.
+ :rtype: Signal based on class `simpleline.event_loop.signals.AbstractSignal` or `None`.
+ """
+ event = self._queue.get()
+ if event.priority == priority:
+ return event
+
+ self._queue.put(event)
+ return None
+
+ def add_source(self, signal_source):
+ """Add new source of signals to this queue.
+
+ This method is mandatory for `enqueue_if_source_belongs()` method.
+ The same source will be added only once.
+
+ :param signal_source: Source of future signals.
+ :type signal_source: Anything which will emit signals in future.
+ """
+ # TODO: Remove when python3-astroid 1.5.3 will be in Fedora
+ # pylint: disable=not-context-manager
+ with self._lock:
+ self._contained_screens.add(signal_source)
+
+ def remove_source(self, signal_source):
+ """Remove signal source from this queue.
+
+ :param signal_source: Source of future signals.
+ :type signal_source: Anything.
+ :raise: EventQueueError"""
+ try:
+ # TODO: Remove when python3-astroid 1.5.3 will be in Fedora
+ # pylint: disable=not-context-manager
+ with self._lock:
+ self._contained_screens.remove(signal_source)
+ except KeyError as e:
+ raise EventQueueError("Can't remove non-existing event source!") from e
+
+ def contains_source(self, signal_source):
+ """Test if `signal_source` belongs to this queue.
+
+ :param signal_source: Source of signals.
+ :type signal_source: Anything.
+ :return: True if signal source belongs to this queue.
+ :rtype: bool
+ """
+ # TODO: Remove when python3-astroid 1.5.3 will be in Fedora
+ # pylint: disable=not-context-manager
+ with self._lock:
+ return signal_source in self._contained_screens
diff --git a/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/event_loop/glib_event_loop.py b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/event_loop/glib_event_loop.py
new file mode 100644
index 0000000..25847e5
--- /dev/null
+++ b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/event_loop/glib_event_loop.py
@@ -0,0 +1,230 @@
+# Glib event queue used by Simpleline application.
+#
+# This class is thread safe.
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+# Author(s): Jiri Konecny
+#
+from collections import namedtuple
+
+import gi
+
+from simpleline.event_loop import AbstractEventLoop, ExitMainLoop
+from simpleline.event_loop.signals import ExceptionSignal
+from simpleline.logging import get_simpleline_logger
+
+gi.require_version("GLib", "2.0")
+
+from gi.repository import GLib # pylint: disable=wrong-import-order, wrong-import-position
+
+log = get_simpleline_logger()
+
+CallbackArgs = namedtuple("CallbackArgs", ["signal", "source", "handlers"])
+
+
+__all__ = ["GLibEventLoop"]
+
+
+class GLibEventLoop(AbstractEventLoop):
+
+ def __init__(self):
+ super().__init__()
+ # Create first loop
+ loop = GLib.MainLoop()
+ self._event_loops = [EventLoopData(loop)]
+ log.debug("GLib event loop is used!")
+
+ @property
+ def active_main_loop(self):
+ """Return GLib mainloop object."""
+ return self._event_loops[-1].loop
+
+ def register_signal_source(self, signal_source):
+ """Register source of signal for actual event queue.
+
+ :param signal_source: Source for future signals.
+ :type signal_source: `simpleline.render.ui_screen.UIScreen`
+ """
+ super().register_signal_source(signal_source)
+ loop_data = self._event_loops[-1]
+ loop_data.sources.add(signal_source)
+
+ def enqueue_signal(self, signal):
+ """Enqueue new event for processing.
+
+ :param signal: signal which you want to add to the event queue for processing
+ :type signal: instance based on AbstractEvent class
+ """
+ if self._force_quit:
+ return
+
+ super().enqueue_signal(signal)
+
+ loop_data = self._find_loop_data_for_source(signal.source)
+ self._register_handlers_to_loop(loop_data.loop, signal)
+
+ def _find_loop_data_for_source(self, source):
+ """Find event loop belonging to this signal source."""
+ for loop_data in reversed(self._event_loops):
+ if source in loop_data.sources:
+ return loop_data
+
+ return self._event_loops[-1]
+
+ def _register_handlers_to_loop(self, event_loop, signal):
+ """Register handlers to the event loop."""
+ context = event_loop.get_context()
+ handlers = []
+
+ if type(signal) in self._handlers: # pylint: disable=unidiomatic-typecheck
+ handlers = self._handlers[type(signal)]
+ elif isinstance(signal, ExceptionSignal):
+ handler_data = self._create_event_handler(self.kill_app_with_traceback, None)
+ handlers = [handler_data]
+
+ # GLib event source which contains handler callback
+ # Every source can hold only one callback
+ source = GLib.idle_source_new()
+ source.set_priority(signal.priority)
+ data = CallbackArgs(signal, source, handlers)
+
+ source.set_callback(self._run_handlers, data)
+ # attach source to the event loop
+ source.attach(context)
+
+ def _run_handlers(self, data):
+ """Run handlers attached to this signal and clean source afterwards."""
+ signal = data.signal
+ source = data.source
+ handlers = data.handlers
+
+ if not self._force_quit:
+ try:
+ for handler in handlers:
+ handler.callback(signal, handler.data)
+ except ExitMainLoop:
+ self._quit_all_loops()
+ except Exception: # pylint: disable=broad-except
+ self.enqueue_signal(ExceptionSignal(self))
+
+ # based on GLib documentation we should clean source
+ # source will be removed from event loop context this way
+ source.destroy()
+
+ self._mark_signal_processed(signal)
+
+ def _quit_all_loops(self):
+ for loop_data in reversed(self._event_loops):
+ loop_data.loop.quit()
+
+ def run(self):
+ """Starts the event loop."""
+ super().run()
+ if len(self._event_loops) != 1:
+ raise ValueError("Can't run event loop multiple times.")
+
+ self._event_loops[0].loop.run()
+ log.debug("Main loop ended. Running callback if set.")
+
+ if self._quit_callback:
+ cb = self._quit_callback.callback
+ cb(self._quit_callback.args)
+
+ def force_quit(self):
+ """Force quit all running event loops.
+
+ Kill all loop including inner loops (modal window).
+ None of the Simpleline events will be processed anymore.
+ """
+ super().force_quit()
+ self._quit_all_loops()
+
+ def execute_new_loop(self, signal):
+ """Starts the new event loop and pass `signal` in it.
+
+ This is required for processing a modal screens.
+
+ :param signal: signal passed to the new event loop
+ :type signal: `AbstractSignal` based class
+ """
+ super().execute_new_loop(signal)
+
+ if self._force_quit:
+ return
+
+ new_context = GLib.MainContext()
+ new_loop = GLib.MainLoop(new_context)
+ loop_data = EventLoopData(new_loop)
+ self._event_loops.append(loop_data)
+
+ self.enqueue_signal(signal)
+ new_loop.run()
+
+ def close_loop(self):
+ """Close active event loop.
+
+ Close an event loop created by the `execute_new_loop()` method.
+ """
+ super().close_loop()
+ old_loop_data = self._event_loops.pop()
+ old_loop_data.loop.quit()
+
+ def process_signals(self, return_after=None):
+ """This method processes incoming async messages.
+
+ Process signals en-queued by the `self.enqueue_signal()` method. Call handlers registered
+ to the signals by the `self.register_signal_handler()` method.
+
+ When `return_after` is specified then wait to the point when this signal is processed.
+ NO warranty that this method will return immediately after the signal was processed!
+
+ Without `return_after` parameter this method will return after all queued signals with
+ the highest priority will be processed.
+
+ The method is NOT thread safe!
+
+ :param return_after: Wait on this signal to be processed.
+ :type return_after: Class of the signal.
+ """
+ super().process_signals(return_after)
+ loop_data = self._event_loops[-1]
+
+ if return_after is not None:
+ ticket_id = self._register_wait_on_signal(return_after)
+
+ while not self._check_if_signal_processed(return_after, ticket_id) and \
+ not self._force_quit:
+ self._iterate_event_loop(loop_data.loop)
+ else:
+ self._iterate_event_loop(loop_data.loop)
+
+ @staticmethod
+ def _iterate_event_loop(event_loop):
+ context = event_loop.get_context()
+ # This is useful for tests
+ wait_on_timeout = False
+ context.iteration(wait_on_timeout)
+
+
+class EventLoopData():
+
+ def __init__(self, loop):
+ super().__init__()
+ self.loop = loop
+ self.sources = set()
diff --git a/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/event_loop/main_loop.py b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/event_loop/main_loop.py
new file mode 100644
index 0000000..bf64a15
--- /dev/null
+++ b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/event_loop/main_loop.py
@@ -0,0 +1,247 @@
+# Default event loop for Simpleline application.
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+# Author(s): Jiri Konecny
+#
+
+from threading import Lock
+
+from simpleline.event_loop import AbstractEventLoop, ExitMainLoop
+from simpleline.event_loop.event_queue import EventQueue
+from simpleline.event_loop.signals import ExceptionSignal
+from simpleline.logging import get_simpleline_logger
+
+log = get_simpleline_logger()
+
+__all__ = ["MainLoop"]
+
+
+class MainLoop(AbstractEventLoop):
+ """Default main event loop for the Simpleline.
+
+ This event loop can be replaced by your event loop by implementing
+ `simpleline.event_loop.AbstractEventLoop` class.
+ """
+
+ def __init__(self):
+ super().__init__()
+ self._active_queue = EventQueue()
+ self._event_queues = [self._active_queue]
+ self._lock = Lock()
+
+ def register_signal_source(self, signal_source):
+ """Register source of signal for actual event queue.
+
+ :param signal_source: Source for future signals.
+ :type signal_source: `simpleline.render.ui_screen.UIScreen`.
+ """
+ super().register_signal_source(signal_source)
+ self._active_queue.add_source(signal_source)
+
+ def run(self):
+ """This methods starts the application.
+
+ Do not use self.mainloop() directly as run() handles all the required exceptions
+ needed to keep nested mainloop working.
+ """
+ super().run()
+ self._run_loop = True
+
+ try:
+ self._mainloop()
+ except ExitMainLoop:
+ pass
+
+ log.debug("Main loop ended. Running callback if set.")
+
+ if self._quit_callback:
+ cb = self._quit_callback.callback
+ cb(self._quit_callback.args)
+
+ def force_quit(self):
+ """Force quit all running event loops.
+
+ Kill all loop including inner loops (modal window).
+ None of the Simpleline events will be processed anymore.
+ """
+ super().force_quit()
+ self._event_queues.clear()
+ self._run_loop = False
+
+ def execute_new_loop(self, signal):
+ """Starts the new event loop and pass `signal` in it.
+
+ This is required for processing a modal screens.
+
+ :param signal: Signal passed to the new event loop.
+ :type signal: The `AbstractSignal` based class.
+ """
+ super().execute_new_loop(signal)
+
+ if self._force_quit:
+ return
+
+ self._active_queue = EventQueue()
+
+ # TODO: Remove when python3-astroid 1.5.3 will be in Fedora
+ # pylint: disable=not-context-manager
+ with self._lock:
+ self._event_queues.append(self._active_queue)
+
+ self.enqueue_signal(signal)
+ self._mainloop()
+ log.debug("Inner loop is closed")
+
+ def close_loop(self):
+ """Close active event loop.
+
+ Close an event loop created by the `execute_new_loop()` method.
+ """
+ super().close_loop()
+ self.process_signals()
+
+ # TODO: Remove when python3-astroid 1.5.3 will be in Fedora
+ # pylint: disable=not-context-manager
+ with self._lock:
+ self._event_queues.pop()
+ try:
+ self._active_queue = self._event_queues[-1]
+ except IndexError:
+ log.error("No more event queues to work with!")
+ raise ExitMainLoop() # pylint: disable=raise-missing-from
+
+ self._run_loop = False
+
+ def enqueue_signal(self, signal):
+ """Enqueue new event for processing.
+
+ Enqueue signal to the most inner queue (nearest to the active queue) where
+ the `signal.source` belongs.
+ If it belongs nowhere enqueue it to the active one.
+
+ This method is thread safe.
+
+ :param signal: Event which you want to add to the event queue for processing.
+ :type signal: Instance based on AbstractEvent class.
+ """
+ if self._force_quit:
+ return
+
+ super().enqueue_signal(signal)
+ # TODO: Remove when python3-astroid 1.5.3 will be in Fedora
+ # pylint: disable=not-context-manager
+ with self._lock:
+ for queue in reversed(self._event_queues):
+ if queue.enqueue_if_source_belongs(signal, signal.source):
+ return
+
+ self._active_queue.enqueue(signal)
+
+ def _mainloop(self):
+ """Single mainloop. Do not use directly, start the application using run()."""
+ # run infinite loop
+ # this will always wait on input processing or similar so it should not busy waiting
+ while self._run_loop:
+ self._process_signals_loop()
+
+ if not self._force_quit:
+ # set back to True to leave outer loop working
+ self._run_loop = True
+
+ def process_signals(self, return_after=None):
+ """This method processes incoming async messages.
+
+ Process signals en-queued by the `self.enqueue_signal()` method. Call handlers
+ registered to the signals by the `self.register_signal_handler()` method.
+
+ When `return_after` is specified then wait to the point when this signal is processed.
+ NO warranty that this method will return immediately after the signal was processed!
+
+ Without `return_after` parameter this method will return after all queued signals
+ with the highest priority will be processed.
+
+ The method is NOT thread safe!
+
+ :param return_after: Wait on this signal to be processed.
+ :type return_after: Class of the signal.
+ """
+ super().process_signals(return_after)
+ if return_after is not None:
+ self._process_signals_with_return(return_after)
+ else:
+ self._process_signals_iteration()
+
+ def _process_signals_with_return(self, return_after):
+ """Process signals until the return_after signal was processed.
+
+ Or the loop quited.
+ """
+ # get unique ID when waiting for the signal
+ unique_id = self._register_wait_on_signal(return_after)
+
+ while self._run_loop:
+ signal = self._active_queue.get()
+
+ # do the signal processing (call handlers)
+ self._process_signal(signal)
+
+ # was our signal processed if yes, return this method
+ if self._check_if_signal_processed(return_after, unique_id):
+ return
+
+ def _process_signals_iteration(self):
+ """Process queued signal and then return."""
+ priority = None
+
+ while not self._active_queue.empty() and self._run_loop:
+ if priority is None:
+ # take first signal to find out the highest priority in queue
+ signal = self._active_queue.get()
+ priority = signal.priority
+ else:
+ # get signal with this priority only
+ signal = self._active_queue.get_top_event_if_priority(priority)
+
+ # Signal with this priority is not available anymore
+ if signal is None:
+ return
+
+ self._process_signal(signal)
+
+ def _process_signals_loop(self):
+ """Process signal until the event loop quited."""
+ while self._run_loop:
+ signal = self._active_queue.get()
+ self._process_signal(signal)
+
+ def _process_signal(self, signal):
+ log.debug("Processing signal %s", signal)
+
+ self._mark_signal_processed(signal)
+
+ if type(signal) in self._handlers: # pylint: disable=unidiomatic-typecheck
+ for handler_data in self._handlers[type(signal)]:
+ try:
+ handler_data.callback(signal, handler_data.data)
+ except ExitMainLoop: # pylint: disable=try-except-raise
+ raise
+ except Exception: # pylint: disable=broad-except
+ self.enqueue_signal(ExceptionSignal(self))
+ elif isinstance(signal, ExceptionSignal):
+ self.kill_app_with_traceback(signal)
diff --git a/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/event_loop/signals.py b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/event_loop/signals.py
new file mode 100644
index 0000000..dd4a5e3
--- /dev/null
+++ b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/event_loop/signals.py
@@ -0,0 +1,110 @@
+# Set of default signals used inside of widgets.
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+# Author(s): Jiri Konecny
+#
+
+from sys import exc_info
+from simpleline.event_loop import AbstractSignal
+
+__all__ = ["ExceptionSignal", "InputReadySignal", "RenderScreenSignal", "CloseScreenSignal",
+ "InputReceivedSignal"]
+
+
+class ExceptionSignal(AbstractSignal):
+ """Emit this signal when exception is raised.
+
+ This class must be created inside of exception handler or `exception_info` must be specified
+ in creation process.
+
+ If you register handler for this exception then the Simpleline's exception handling
+ is disabled!
+ """
+
+ def __init__(self, source, exception_info=None):
+ """Create exception signal with higher priority (-20) than other signals.
+
+ :param source: source of this signal
+ :type source: class which emits this signal
+
+ :param exception_info: if specified raise your exception, otherwise create exception here;
+ to create exception here it needs to be created inside of exception
+ handler
+ :type exception_info: output of `sys.exc_info()` method
+ """
+ super().__init__(source, priority=-20)
+ if exception_info:
+ self.exception_info = exception_info
+ else:
+ self.exception_info = exc_info()
+
+
+class InputReadySignal(AbstractSignal):
+ """Input from user is ready for processing."""
+ def __init__(self, source, input_handler_source, data, priority=0, success=True):
+ """Store user input inside of this signal
+
+ Read the data from user input in `data` attribute.
+
+ The only way how a user should ask for input is to use InputHandler and inherited classes.
+ The input_handler_source param must be set but this signal instance can be attached to
+ another source object which is registered to a specific event loop.
+
+ If no requester (object who uses InputHandler) is specified then source and
+ input_handler_source will both point to InputHandler instance.
+
+ :param source: Source of this signal.
+ :type source: Any object.
+
+ :param input_handler_source: InputHandler who is asking for input.
+ :type input_handler_source: The `simpleline.input.input_handler.InputHandler` based
+ instance.
+
+ :param data: User input data.
+ :type data: str
+
+ :param priority: Priority of this event.
+ :type priority: Int greater than 0.
+
+ :param success: Was the input successful? True on successful input False otherwise.
+ :type success: bool
+ """
+ super().__init__(source, priority=priority)
+ self.input_handler_source = input_handler_source
+ self.data = data
+ self.success = success
+
+
+class InputReceivedSignal(AbstractSignal):
+ """Raw input received.
+
+ This signal will be further processed and InputReadySignal should be enqueued soon.
+ Most probably you are looking for InputReadySignal instead.
+ """
+ def __init__(self, source, data, priority=0):
+ super().__init__(source, priority=priority)
+ self.data = data
+
+
+class RenderScreenSignal(AbstractSignal):
+ """Render UIScreen to terminal."""
+
+
+class CloseScreenSignal(AbstractSignal):
+ """Close current screen."""
diff --git a/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/event_loop/ticket_machine.py b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/event_loop/ticket_machine.py
new file mode 100644
index 0000000..6234720
--- /dev/null
+++ b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/event_loop/ticket_machine.py
@@ -0,0 +1,81 @@
+# Ticket machine synchronization class.
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+# Author(s): Jiri Konecny
+#
+
+
+class TicketMachine():
+ """Hold signals processed by the event loop if someone wait on them.
+
+ This is useful when recursive process events will skip required signal.
+ """
+
+ def __init__(self):
+ self._lines = {}
+ self._counter = 0
+
+ def take_ticket(self, line_id):
+ """Take ticket (id) and go line (processing events).
+
+ Use `check_ticket` if you are ready to go.
+
+ :param line_id: Line where you are waiting.
+ :type line_id: Anything.
+ """
+ obj_id = self._counter
+ if line_id not in self._lines:
+ self._lines[line_id] = {obj_id: False}
+ else:
+ self._lines[line_id][obj_id] = False
+
+ self._counter += 1
+ return obj_id
+
+ def check_ticket(self, line, unique_id):
+ """Check if you are ready to go.
+
+ If True the unique_id is not valid anymore.
+
+ :param unique_id: Your id used to identify you in the line.
+ :type unique_id: int
+
+ :param line: Line where you are waiting.
+ :type line: Anything.
+
+ :return: True if the ticket was already marked, False otherwise
+ :rtype: bool
+ """
+ if self._lines[line][unique_id]:
+ return self._lines[line].pop(unique_id)
+
+ return False
+
+ def mark_line_to_go(self, line):
+ """All in the `line` are ready to go.
+
+ Mark all tickets in the line as True.
+
+ :param line: Line which should processed.
+ :type line: Anything.
+ """
+ if line in self._lines:
+ our_line = self._lines[line]
+ for key in our_line:
+ our_line[key] = True
diff --git a/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/global_configuration.py b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/global_configuration.py
new file mode 100644
index 0000000..1fa91a7
--- /dev/null
+++ b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/global_configuration.py
@@ -0,0 +1,112 @@
+# Global configuration for the whole application.
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+
+from getpass import getpass
+
+__all__ = ["GlobalConfiguration"]
+
+DEFAULT_WIDTH = 80
+DEFAULT_PASSWORD_FUNC = getpass
+
+
+class GlobalConfiguration():
+ """Class for global configuration of application defaults.
+
+ All stored data are persistent between App.initialize() calls and can be used before this call.
+ """
+
+ def __init__(self):
+ self._width = DEFAULT_WIDTH
+ self._getpass = DEFAULT_PASSWORD_FUNC
+ self._run_with_empty_stack = False
+
+ @property
+ def width(self):
+ """Get width of the application.
+
+ :returns: int
+ """
+ return self._width
+
+ @width.setter
+ def width(self, width):
+ """Set width of the application.
+
+ :param width: Number of characters which can be printed to one line.
+ :type width: int
+ """
+ self._width = width
+
+ def clear_width(self):
+ """Clear user defined width and set the default.
+
+ Default: 80 characters
+ """
+ self._width = DEFAULT_WIDTH
+
+ @property
+ def password_function(self):
+ """Get function to get user passwords from a console.
+
+ :returns: Function with one argument which is text representation of prompt.
+ """
+ return self._getpass
+
+ @password_function.setter
+ def password_function(self, password_func):
+ """Set function to get user passwords from a console.
+
+ :param password_func: Function to get password from a command line.
+ :type password_func: Function with one argument which is text representation of prompt.
+ """
+ self._getpass = password_func
+
+ def clear_password_function(self):
+ """Clear user defined password function and set the default.
+
+ Default: getpass.getpass function
+ """
+ self._getpass = getpass
+
+ @property
+ def should_run_with_empty_stack(self):
+ """Should test on empty screen stack when starting event loop.
+
+ :returns: If False the App.run() call will end with an exception (default), True otherwise.
+ """
+ return self._run_with_empty_stack
+
+ @should_run_with_empty_stack.setter
+ def should_run_with_empty_stack(self, value):
+ """Set if the App.run() call should end with an exception when screen stack is empty.
+
+ This can be valuable when you want to schedule a screen later by an other thread.
+
+ :param value: If False the App.run() call will end with an exception, if True it will
+ run with nothing displayed.
+ """
+ self._run_with_empty_stack = value
+
+ def clear_should_run_with_empty_stack(self):
+ """Clear user defined test to run with an empty screen stack.
+
+ Default: False
+ """
+ self._run_with_empty_stack = False
diff --git a/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/input/__init__.py b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/input/__init__.py
new file mode 100644
index 0000000..e632883
--- /dev/null
+++ b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/input/__init__.py
@@ -0,0 +1,22 @@
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+
+from simpleline.input.input_handler import InputHandler, PasswordInputHandler
+
+__all__ = ["InputHandler", "PasswordInputHandler"]
diff --git a/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/input/__pycache__/__init__.cpython-314.pyc b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/input/__pycache__/__init__.cpython-314.pyc
new file mode 100644
index 0000000..e31aa92
Binary files /dev/null and b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/input/__pycache__/__init__.cpython-314.pyc differ
diff --git a/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/input/__pycache__/input_handler.cpython-314.pyc b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/input/__pycache__/input_handler.cpython-314.pyc
new file mode 100644
index 0000000..d7ad19f
Binary files /dev/null and b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/input/__pycache__/input_handler.cpython-314.pyc differ
diff --git a/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/input/__pycache__/input_threading.cpython-314.pyc b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/input/__pycache__/input_threading.cpython-314.pyc
new file mode 100644
index 0000000..b7a94ac
Binary files /dev/null and b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/input/__pycache__/input_threading.cpython-314.pyc differ
diff --git a/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/input/input_handler.py b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/input/input_handler.py
new file mode 100644
index 0000000..2528a01
--- /dev/null
+++ b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/input/input_handler.py
@@ -0,0 +1,292 @@
+# Handle user input
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+
+import sys
+
+from simpleline import App
+from simpleline.event_loop.signals import InputReadySignal
+from simpleline.render.widgets import TextWidget
+from simpleline.input.input_threading import InputThreadManager, InputRequest
+
+__all__ = ["InputHandler", "PasswordInputHandler"]
+
+
+class InputHandler():
+
+ def __init__(self, callback=None, source=None):
+ """Class to handle input from the terminal.
+
+ This class is designed to be instantiated on place where it should be used.
+ The main method is `get_input()` which is non-blocking asynchronous call. It can be used
+ as synchronous call be calling the `wait_on_input` method.
+
+ To get result from this class use the `value` property.
+
+ :param callback: You can specify callback which will be called when user give input.
+ :type callback: Callback function with one argument which will be user input.
+
+ :param source: Source of this input. It will be helpful in case of debugging an issue.
+ :type source: Class which will process an input from this InputHandler.
+ """
+ super().__init__()
+ self._input = None
+ self._input_callback = callback
+ self._input_received = False
+ self._input_successful = False
+ self._skip_concurrency_check = False
+ self._source = source
+
+ App.get_event_loop().register_signal_handler(InputReadySignal,
+ self._input_received_handler)
+
+ def _input_received_handler(self, signal, args):
+ if signal.input_handler_source != self:
+ return
+
+ self._input_received = True
+ self._input_successful = signal.success
+
+ if not self._input_successful:
+ return
+
+ self._input = signal.data
+
+ # call async callback
+ if self._input_callback is not None:
+ cb = self._input_callback
+ self._input_callback = None
+
+ cb(self._input)
+
+ @property
+ def value(self):
+ """Return user input.
+
+ :returns: String or None if no is input received.
+ """
+ return self._input
+
+ @property
+ def source(self):
+ """Get source of this input.
+
+ :returns: Anything probably UIScreen.
+ """
+ return self._source
+
+ @property
+ def skip_concurrency_check(self):
+ """Is this InputHandler skipping concurrency check?
+
+ :returns bool
+ """
+ return self._skip_concurrency_check
+
+ @skip_concurrency_check.setter
+ def skip_concurrency_check(self, value):
+ """Set if this InputHandler should skip concurrency check.
+
+ Note if you skip this check, you can have unexpected behavior. Use with caution.
+
+ :param value: True to skip the check, False if not.
+ """
+ self._skip_concurrency_check = value
+
+ def set_callback(self, callback):
+ """Set a callback to get user input asynchronously.
+
+ :param callback: Callback called when user write their input.
+ :type callback: Method with 1 argument which is user input: def cb(user_input)
+ """
+ self._input_callback = callback
+
+ def input_received(self):
+ """Was user input already received?
+
+ :returns: True if yes, False otherwise.
+ """
+ return self._input_received
+
+ def wait_on_input(self):
+ """Blocks execution till the user input is received.
+
+ Events will works as expected during this blocking.
+
+ Please check the `input_successful` method to test the input.
+ """
+ # we already received input from user
+ if self._input_received:
+ return
+
+ while not self._input_received:
+ App.get_event_loop().process_signals(InputReadySignal)
+
+ def input_successful(self):
+ """Was input successful?
+
+ :returns: bool
+ """
+ return self._input_successful
+
+ def get_input(self, prompt):
+ """Use prompt to ask for user input and wait (non-blocking) on user input.
+
+ This is an asynchronous call. If you want to wait for user input then use
+ the `wait_on_input` method. If you want to get results asynchronously then register
+ callback in constructor or by the `set_callback` method.
+
+ Check if user input was already received can be done by the `input_received` method call.
+
+ :param prompt: Ask user what you want to get.
+ :type prompt: String or Prompt instance.
+
+ :returns: User input.
+ :rtype: str
+ """
+ self._clear_input()
+ self._invoke_input_thread(prompt)
+
+ def _invoke_input_thread(self, prompt):
+ thread_object = self.create_thread_object(prompt)
+ InputThreadManager.get_instance().start_input_thread(thread_object,
+ not self._skip_concurrency_check)
+
+ def create_thread_object(self, prompt):
+ """Create thread object containing all the information how to get user input.
+
+ :returns: Instance of class inherited from `simpleline.input.InputThread`.
+ """
+ return InputHandlerRequest(App.get_configuration().width, prompt, self)
+
+ def _clear_input(self):
+ self._input_received = False
+ self._input = None
+
+
+class InputHandlerRequest(InputRequest):
+ """This is thread object to get input from user without blocking main thread."""
+
+ def __init__(self, width, prompt, input_handler):
+ """Create request object to get input in InputThreadManager.
+
+ :param width: Width of the screen prompt.
+ :type width: int
+
+ :param prompt: Input prompt.
+ :type prompt: Instance of `simpleline.render.prompt.Prompt` class.
+
+ :param input_handler: InputHandler instance which created this object.
+ :type input_handler: InputHandler based instance.
+ """
+ super().__init__(input_handler, input_handler.source)
+ self._width = width
+ self._prompt = prompt
+
+ def get_input(self):
+ """This method is responsible for interruptable user input.
+
+ It is expected to be used in a thread started on demand
+ and returns the input via the communication Queue.
+ """
+ # lock acquired, we can run input
+ try:
+ data = self._ask_input()
+ except EOFError:
+ data = ""
+
+ return data
+
+ def text_prompt(self):
+ widget = TextWidget(str(self._prompt))
+ widget.render(self._width)
+ lines = widget.get_lines()
+ return "\n".join(lines) + " "
+
+ def _ask_input(self):
+ text_prompt = self.text_prompt()
+ sys.stdout.write(text_prompt)
+ sys.stdout.flush()
+
+ return self._get_input()
+
+ @staticmethod
+ def _get_input():
+ return input()
+
+
+class PasswordInputHandler(InputHandler):
+
+ def __init__(self, callback=None, source=None):
+ """Class to handle hidden password input from the terminal.
+
+ This class is designed to be instantiated on place where it should be used.
+ The main method is `get_input()` which is non-blocking asynchronous call. It can be used
+ as synchronous call be calling the `wait_on_input` method.
+
+ To get result from this class use the `value` property.
+
+ :param callback: You can specify callback which will be called when user give input.
+ :type callback: Callback function with one argument which will be user input.
+
+ :param source: Source of this input. It will be helpful in case of debugging an issue.
+ :type source: Class which will process an input from this InputHandler.
+ """
+ super().__init__(callback=callback, source=source)
+ self._getpass_func = App.get_configuration().password_function
+
+ def set_pass_func(self, getpass_func):
+ """Set a function for getting passwords."""
+ if not getpass_func:
+ return
+
+ self._getpass_func = getpass_func
+
+ def create_thread_object(self, prompt):
+ """Return PasswordInputThread for getting user password."""
+ return PasswordInputHandlerRequest(App.get_configuration().width, prompt, self,
+ self._getpass_func)
+
+
+class PasswordInputHandlerRequest(InputHandlerRequest):
+ """Similar as InputHandlerRequest but don't echo user keys."""
+
+ def __init__(self, width, prompt, input_handler, getpass_func):
+ """Create request object to get password input in InputThreadManager.
+
+ :param width: Width of the screen prompt.
+ :type width: int
+
+ :param prompt: Input prompt.
+ :type prompt: Instance of `simpleline.render.prompt.Prompt` class.
+
+ :param input_handler: InputHandler instance which created this object.
+ :type input_handler: InputHandler based instance.
+
+ :param getpass_func: Function to get user password.
+ :type getpass_func: Function which gets prompt as only parameter and returns user input
+ string.
+ """
+ super().__init__(width, prompt, input_handler)
+ self._getpass_func = getpass_func
+
+ def _ask_input(self):
+ text_prompt = self.text_prompt()
+
+ return self._getpass_func(text_prompt)
diff --git a/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/input/input_threading.py b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/input/input_threading.py
new file mode 100644
index 0000000..b8926c8
--- /dev/null
+++ b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/input/input_threading.py
@@ -0,0 +1,238 @@
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+
+import threading
+from abc import ABCMeta, abstractmethod
+
+from simpleline import App
+from simpleline.logging import get_simpleline_logger
+from simpleline.event_loop.signals import InputReceivedSignal, InputReadySignal
+
+log = get_simpleline_logger()
+
+
+INPUT_THREAD_NAME = "SimplelineInputThread"
+
+
+class InputThreadManager():
+ """Manager object for input threads.
+
+ This manager helps with concurrent user input (still you really shouldn't do that).
+ """
+
+ __instance = None
+
+ def __init__(self):
+ super().__init__()
+ self._input_stack = []
+ self._processing_input = False
+
+ @classmethod
+ def create_new_instance(cls):
+ instance = InputThreadManager()
+ cls.__instance = instance
+
+ instance._post_init_configuration() # pylint: disable=protected-access
+
+ def _post_init_configuration(self):
+ # pylint: disable=protected-access
+ App.get_event_loop().register_signal_handler(InputReceivedSignal,
+ self.__instance._input_received_handler)
+
+ @classmethod
+ def get_instance(cls):
+ if not cls.__instance:
+ cls.create_new_instance()
+
+ return cls.__instance
+
+ def _input_received_handler(self, signal, args):
+ thread_object = self._input_stack.pop()
+ thread_object.emit_input_ready_signal(signal.data)
+
+ if thread_object.thread:
+ thread_object.thread.join()
+
+ # wait until used object ends
+ for t in self._input_stack:
+ t.emit_failed_input_ready_signal()
+ if t.thread:
+ t.thread.join()
+
+ # remove all other items waiting for input
+ self._input_stack.clear()
+ self._processing_input = False
+
+ def start_input_thread(self, input_thread_object, concurrent_check=True):
+ """Start input thread to get user input.
+
+ :param input_thread_object: Input thread object based on InputThread class.
+ :param concurrent_check: Should the concurrent thread check be fatal? (default True).
+ """
+ self._input_stack.append(input_thread_object)
+ self._check_input_thread_running(concurrent_check)
+ self._start_user_input_async()
+
+ def _check_input_thread_running(self, raise_concurrent_check):
+ if len(self._input_stack) != 1:
+ if not raise_concurrent_check:
+ log.warning("Asking for multiple inputs with concurrent check bypassed, "
+ "last who asked wins! Others are dropped.")
+ else:
+ msg = ""
+ for t in self._input_stack:
+ requester_source = t.requester_source or "Unknown"
+ msg += "Input handler: {} Input requester: {}\n".format(t.source,
+ requester_source)
+
+ msg.rstrip()
+
+ raise KeyError("Can't run multiple input threads at the same time!\n"
+ "Asking for input:\n"
+ "{}".format(msg))
+
+ def _start_user_input_async(self):
+ thread_object = self._input_stack[-1]
+
+ if self._processing_input:
+ self._print_new_prompt(thread_object)
+ return
+
+ thread_object.initialize_thread()
+ self._processing_input = True
+ thread_object.start_thread()
+
+ @staticmethod
+ def _print_new_prompt(thread_object):
+ prompt = thread_object.text_prompt()
+
+ # print new prompt
+ print(prompt, end="")
+
+
+class InputRequest(metaclass=ABCMeta):
+ """Base input request class.
+
+ This should be overloaded for every InputHandler class. Purpose of this class is to print
+ prompt and get input from user.
+
+ The `run_input` method is the entry point for this class. Output from this method must be
+ a user input.
+ The `text_prompt` method is used to get textual representation of a prompt. This will be used
+ on concurrent input to replace existing prompt to get new input.
+
+ WARNING:
+ The `run_input` method will run in a separate thread!
+ """
+
+ def __init__(self, source, requester_source=None):
+ super().__init__()
+ self._source = source
+ self._requester_source = requester_source
+ self.thread = None
+
+ @property
+ def source(self):
+ """Get direct source of this input request.
+
+ :returns: InputHandler instance.
+ """
+ return self._source
+
+ @property
+ def requester_source(self):
+ """Get requester -- source of this input.
+
+ :returns: Anything probably UIScreen based instance.
+ """
+ return self._requester_source
+
+ def emit_input_ready_signal(self, input_data):
+ """Emit the InputReadySignal signal with collected input data.
+
+ :param input_data: Input data received.
+ :type input_data: str
+ """
+ handler_source = self.source
+ signal_source = self._get_request_source()
+
+ new_signal = InputReadySignal(source=signal_source, input_handler_source=handler_source,
+ data=input_data, success=True)
+ App.get_event_loop().enqueue_signal(new_signal)
+
+ def emit_failed_input_ready_signal(self):
+ """Emit the InputReadySignal with failed state."""
+ handler_source = self.source
+ signal_source = self._get_request_source()
+
+ new_signal = InputReadySignal(source=signal_source, input_handler_source=handler_source,
+ data="", success=False)
+ App.get_event_loop().enqueue_signal(new_signal)
+
+ def _get_request_source(self):
+ """Get user input request source.
+
+ That means object who is using InputHandler.
+ If this object is not specified then return InputHandler as a source.
+ """
+ return self.requester_source or self.source
+
+ def initialize_thread(self):
+ """Initialize thread for this input request.
+
+ Do not call this directly! Will be called by InputThreadManager.
+ """
+ self.thread = threading.Thread(name=INPUT_THREAD_NAME, target=self.run)
+ self.thread.daemon = True
+
+ def start_thread(self):
+ """Start input thread.
+
+ Do not call this directly! Will be called by InputThreadManager.
+ """
+ self.thread.start()
+
+ def run(self):
+ """Run the `run_input` method and propagate input outside.
+
+ Do not call this method directly. It will be called by InputThreadManager.
+ """
+ data = self.get_input()
+
+ App.get_event_loop().enqueue_signal(InputReceivedSignal(self, data))
+
+ @abstractmethod
+ def text_prompt(self):
+ """Get text representation of the user prompt.
+
+ This will be used to get high priority input.
+
+ :returns: String representation of the prompt or None if no prompt is present.
+ """
+ return None
+
+ @abstractmethod
+ def get_input(self):
+ """Print prompt and get an input from user.
+
+ ..NOTE: Overload this method in your class.
+
+ Return this input from a function.
+ """
+ return ""
diff --git a/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/logging.py b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/logging.py
new file mode 100644
index 0000000..f22ddaa
--- /dev/null
+++ b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/logging.py
@@ -0,0 +1,39 @@
+# Logging functions and methods used by Simpleline.
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+# Author(s): Jiri Konecny
+#
+
+
+import logging
+
+
+SIMPLELINE_LOGGER = "simpleline"
+
+
+def setup_logging():
+ """Set proper logging for a library"""
+ log = get_simpleline_logger()
+ null_hd = logging.NullHandler()
+ log.addHandler(null_hd)
+
+
+def get_simpleline_logger():
+ """Return logging instance that can be used in the application."""
+ return logging.getLogger(SIMPLELINE_LOGGER)
diff --git a/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/render/__init__.py b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/render/__init__.py
new file mode 100644
index 0000000..20125e6
--- /dev/null
+++ b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/render/__init__.py
@@ -0,0 +1,31 @@
+# Module with classes to handle rendering and input.
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+# Author(s): Jiri Konecny
+#
+
+from simpleline.errors import SimplelineError
+
+
+class RenderError(SimplelineError):
+ """Exception raised when error in rendering happens."""
+
+
+class RenderUnexpectedError(RenderError):
+ """Exception raised when something goes really wrong."""
diff --git a/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/render/__pycache__/__init__.cpython-314.pyc b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/render/__pycache__/__init__.cpython-314.pyc
new file mode 100644
index 0000000..e0735a0
Binary files /dev/null and b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/render/__pycache__/__init__.cpython-314.pyc differ
diff --git a/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/render/__pycache__/adv_widgets.cpython-314.pyc b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/render/__pycache__/adv_widgets.cpython-314.pyc
new file mode 100644
index 0000000..d1d3331
Binary files /dev/null and b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/render/__pycache__/adv_widgets.cpython-314.pyc differ
diff --git a/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/render/__pycache__/containers.cpython-314.pyc b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/render/__pycache__/containers.cpython-314.pyc
new file mode 100644
index 0000000..14b2b31
Binary files /dev/null and b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/render/__pycache__/containers.cpython-314.pyc differ
diff --git a/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/render/__pycache__/prompt.cpython-314.pyc b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/render/__pycache__/prompt.cpython-314.pyc
new file mode 100644
index 0000000..efd5efa
Binary files /dev/null and b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/render/__pycache__/prompt.cpython-314.pyc differ
diff --git a/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/render/__pycache__/screen_handler.cpython-314.pyc b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/render/__pycache__/screen_handler.cpython-314.pyc
new file mode 100644
index 0000000..e0c7ea9
Binary files /dev/null and b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/render/__pycache__/screen_handler.cpython-314.pyc differ
diff --git a/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/render/__pycache__/screen_scheduler.cpython-314.pyc b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/render/__pycache__/screen_scheduler.cpython-314.pyc
new file mode 100644
index 0000000..fb2dfce
Binary files /dev/null and b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/render/__pycache__/screen_scheduler.cpython-314.pyc differ
diff --git a/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/render/__pycache__/screen_stack.cpython-314.pyc b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/render/__pycache__/screen_stack.cpython-314.pyc
new file mode 100644
index 0000000..3bc9f28
Binary files /dev/null and b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/render/__pycache__/screen_stack.cpython-314.pyc differ
diff --git a/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/render/__pycache__/widgets.cpython-314.pyc b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/render/__pycache__/widgets.cpython-314.pyc
new file mode 100644
index 0000000..d3a5114
Binary files /dev/null and b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/render/__pycache__/widgets.cpython-314.pyc differ
diff --git a/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/render/adv_widgets.py b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/render/adv_widgets.py
new file mode 100644
index 0000000..e8399b4
--- /dev/null
+++ b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/render/adv_widgets.py
@@ -0,0 +1,253 @@
+# Advanced widgets
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+
+import sys
+
+from simpleline.render import widgets
+from simpleline.render.containers import WindowContainer
+from simpleline.render.prompt import Prompt
+from simpleline.render.screen import UIScreen, InputState
+from simpleline.input.input_handler import PasswordInputHandler
+from simpleline.utils.i18n import _, N_, C_
+
+__all__ = ["ErrorDialog", "GetInputScreen", "GetPasswordInputScreen", "HelpScreen",
+ "PasswordDialog", "YesNoDialog"]
+
+
+class ErrorDialog(UIScreen):
+ """Dialog screen for reporting errors to user."""
+
+ def __init__(self, message):
+ """
+ :param message: the message to show to the user
+ :type message: str
+ """
+ super().__init__()
+ self.title = N_("Error")
+ self._message = message
+
+ def refresh(self, args=None):
+ super().refresh(args)
+ text = widgets.TextWidget(self._message)
+ self.window.add_with_separator(widgets.CenterWidget(text))
+
+ def prompt(self, args=None):
+ return Prompt(_("Press %s to exit") % Prompt.ENTER)
+
+ def input(self, args, key):
+ """This dialog is closed by any input.
+
+ And causes the program to quit.
+ """
+ sys.exit(1)
+
+
+class PasswordDialog(UIScreen):
+ """Dialog screen for password input."""
+
+ def __init__(self, message=None):
+ """
+ :param message: password prompt question
+ :type message: string
+ """
+ super().__init__()
+ self.title = N_("Password")
+ self._message = message or _("Enter your passphrase")
+ self._password = None
+
+ def refresh(self, args=None):
+ super().refresh(args)
+ text = widgets.TextWidget(self._message)
+ self.window.add_with_separator(widgets.CenterWidget(text))
+
+ def prompt(self, args=None):
+ handler = PasswordInputHandler(source=self)
+ if self.password_func:
+ handler.set_pass_func(self.password_func)
+
+ handler.get_input(_("Passphrase: "))
+ handler.wait_on_input()
+
+ if not handler.input_successful():
+ return None
+
+ self._password = handler.value
+
+ # this may seem innocuous, but it's really a giant hack; we should
+ # not be calling close() from prompt(), but the input handling code
+ # in the TUI is such that without this very simple workaround, we
+ # would be forever pelting users with a prompt to enter their pw
+ self.close()
+ return None
+
+ @property
+ def answer(self):
+ """The response can be None (no response) or the password entered."""
+ return self._password
+
+ def input(self, args, key):
+ if key:
+ self._password = key
+ return InputState.PROCESSED_AND_CLOSE
+
+ return InputState.DISCARDED
+
+
+class YesNoDialog(UIScreen):
+ """Dialog screen for Yes - No questions."""
+
+ def __init__(self, message):
+ """
+ :param message: the message to show to the user
+ :type message: unicode
+ """
+ super().__init__()
+ self.title = N_("Question")
+ self._message = message
+ self._response = None
+
+ def refresh(self, args=None):
+ super().refresh(args)
+ text = widgets.TextWidget(self._message)
+ self.window.add_with_separator(widgets.CenterWidget(text))
+
+ def prompt(self, args=None):
+ return Prompt(_("Please respond '%(yes)s' or '%(no)s'") % {
+ # TRANSLATORS: 'yes' as positive reply
+ "yes": C_('TUI|Spoke Navigation', 'yes'),
+ # TRANSLATORS: 'no' as negative reply
+ "no": C_('TUI|Spoke Navigation', 'no')
+ })
+
+ def input(self, args, key):
+ # TRANSLATORS: 'yes' as positive reply
+ if key == C_('TUI|Spoke Navigation', 'yes'):
+ self._response = True
+ return InputState.PROCESSED_AND_CLOSE
+
+ # TRANSLATORS: 'no' as negative reply
+ if key == C_('TUI|Spoke Navigation', 'no'):
+ self._response = False
+ return InputState.PROCESSED_AND_CLOSE
+
+ return InputState.DISCARDED
+
+ @property
+ def answer(self):
+ """The response can be True (yes), False (no) or None (no response)."""
+ return self._response
+
+
+class HelpScreen(UIScreen):
+ """Screen to display a help message."""
+
+ def __init__(self, help_path):
+ """
+ :param help_path: help file name
+ :type help_path: str
+ """
+ super().__init__()
+ self.title = N_("Help")
+ self.help_path = help_path
+
+ def refresh(self, args=None):
+ """ Show the help. """
+ super().refresh(args)
+ help_message = _("The help is not available.")
+
+ if self.help_path:
+ with open(self.help_path, 'r') as f:
+ help_message = f.read()
+
+ self.window.add_with_separator(widgets.TextWidget(help_message))
+
+ def input(self, args, key):
+ """ Handle user input. """
+ return InputState.PROCESSED_AND_CLOSE
+
+ def prompt(self, args=None):
+ return Prompt(_("Press %s to return") % Prompt.ENTER)
+
+
+class GetInputScreen(UIScreen):
+ """Screen for getting user input."""
+
+ def __init__(self, message):
+ """
+ :param message: Prompt printed before user input.
+ :type message: str
+ """
+ super().__init__()
+ self._message = message
+ self._value = None
+ self._conditions = []
+
+ @property
+ def value(self):
+ """User input."""
+ return self._value
+
+ def add_acceptance_condition(self, acceptance_function, args=None):
+ """Add acceptance condition to the conditions list.
+
+ :param acceptance_function: Functions that accepts or rejects a user input.
+ :type acceptance_function: `function(input, args) -> bool` - function which takes
+ user input (string) and arguments (`args`) and return True when
+ input is accepted or False if rejected so we will ask for
+ a new input.
+
+ :param args: Second argument for `acceptance_function` the first one will be user input.
+ :type args: Anything.
+ """
+ self._conditions.append((acceptance_function, args))
+
+ def clear_acceptance_conditions(self):
+ """Clear list of the acceptance conditions."""
+ self._conditions.clear()
+
+ def refresh(self, args=None):
+ super().refresh(args)
+ self._window = WindowContainer()
+
+ def prompt(self, args=None):
+ return Prompt(message=self._message)
+
+ def input(self, args, key):
+ if not self._test_input(key):
+ return InputState.DISCARDED
+
+ self._value = key
+
+ return InputState.PROCESSED_AND_CLOSE
+
+ def _test_input(self, key):
+ for f, args in self._conditions:
+ if not f(key, args):
+ return False
+
+ return True
+
+
+class GetPasswordInputScreen(GetInputScreen):
+ """Screen for getting user password input."""
+
+ def __init__(self, message):
+ super().__init__(message)
+ self.hide_user_input = True
diff --git a/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/render/containers.py b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/render/containers.py
new file mode 100644
index 0000000..fb233b2
--- /dev/null
+++ b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/render/containers.py
@@ -0,0 +1,475 @@
+# Widgets for holding other widgets.
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+
+from math import ceil
+
+from simpleline.render.widgets import Widget, TextWidget, SeparatorWidget
+
+from simpleline.logging import get_simpleline_logger
+
+__all__ = ["ListRowContainer", "ListColumnContainer", "WindowContainer"]
+
+log = get_simpleline_logger()
+
+
+class Container(Widget):
+ """Base class for containers which will do positioning of the widgets."""
+
+ def __init__(self, items=None, numbering=True):
+ """Construct Container.
+
+ :param items: List of items for positioning in this Container. Callback
+ can't be specified this way.
+ :type items: List of items for rendering.
+
+ :param numbering: Enable/disable automatic numbering (labels) for items.
+ Enabled by default (True).
+ :type numbering: bool
+ """
+ super().__init__()
+ self._key_pattern = None
+ self._items = []
+ if items:
+ for i in items:
+ self._items.append(ContainerItem(i))
+
+ if numbering:
+ self._key_pattern = KeyPattern()
+ else:
+ self._key_pattern = None
+
+ @property
+ def size(self):
+ """Return items count."""
+ return len(self._items)
+
+ @property
+ def key_pattern(self):
+ """Return key pattern which will be used for items numbering.
+
+ Will return `None` if not set.
+ """
+ return self._key_pattern
+
+ @key_pattern.setter
+ def key_pattern(self, key_pattern):
+ """Set the key pattern object which will be used for items numbering.
+
+ Setting `None` will stop doing numbering.
+ """
+ self._key_pattern = key_pattern
+
+ def add(self, item, callback=None, data=None):
+ """Add item to the Container.
+
+ :param item: Add item to this container.
+ :type item: Could be item (based on `simpleline.render.widgets.Widget`)
+ or other container (based on `simpleline.render.containers.Container`).
+
+ :param callback: Add callback for this item. This callback will be called when user
+ activate this `item`.
+ :type callback: function ``func(data)``.
+
+ :param data: Data which will be passed to the callback.
+ :param data: Anything.
+
+ :returns: ID of the item in this Container.
+ :rtype: int
+ """
+ self._items.append(ContainerItem(item, callback, data))
+ return len(self._items) - 1
+
+ def process_user_input(self, key):
+ """Process input from the user if any of the items in the list was called.
+
+ This method must be called in `UIScreen.input()` method if list widget should call
+ the callbacks.
+
+ :param key: Key pressed from user.
+ :type key: str
+
+ :returns: True if key was processed. False otherwise.
+ """
+ if not self._key_pattern:
+ return False
+
+ if not isinstance(key, str):
+ return False
+
+ res = self._key_pattern.translate_input_to_widget_id(key)
+ if res is not None and res >= 0:
+ try:
+ item = self._items[res]
+ if item.callback is not None:
+ item.callback(item.data)
+ return True
+ except IndexError: # container widget with this id doesn't exists
+ return False
+
+ return False
+
+ def create_number_label(self, item_id):
+ """Create TextWidget from KeyPattern.
+
+ :param item_id: Create label for item with this id.
+ :type item_id: int
+
+ :returns: Widget with label for the item with item_id.
+ :rtype: `simpleline.render.widgets.TextWidget` instance.
+ """
+ number_widget = TextWidget(self._key_pattern.get_widget_label(item_id))
+ return number_widget
+
+
+class WindowContainer(Container):
+ """Base container for screens.
+
+ This can hold other containers or Widgets for rendering.
+ """
+
+ def __init__(self, title=None):
+ """Construct base container for screens.
+
+ This container doesn't have numbering support. Input other containers in it to
+ allow numbering and input processing.
+
+ :param title: Title line with separator after this title.
+ :type title: str
+ """
+ super().__init__(numbering=False)
+ self._title = title
+
+ def add_with_separator(self, item, callback=None, data=None, blank_lines=1):
+ """Add widget and after widget add blank line.
+
+ This method will call
+ `self.add(item, callback, data)`
+ `self.add_separator(lines)`
+
+ :param item: Add item to this container.
+ :type item: Could be item (based on `simpleline.render.widgets.Widget`)
+ or other container (based on `simpleline.render.containers.Container`).
+
+ :param callback: Add callback for this item. This callback will be called when user
+ activate this `item`.
+ :type callback: function ``func(data)``.
+
+ :param data: Data which will be passed to the callback.
+ :param data: Anything.
+
+ :param blank_lines: How many blank lines should be printed.
+ :type blank_lines: int greater than 0.
+
+ :returns: ID of the item in this Container.
+ :rtype: int
+ """
+ item_id = self.add(item, callback, data)
+ self.add_separator(blank_lines)
+
+ return item_id
+
+ def add_separator(self, lines=1):
+ """Add blank lines between widgets.
+
+ :param lines: How many blank lines should be printed.
+ :type lines: int greater than 0.
+ """
+ self.add(SeparatorWidget(lines))
+
+ @property
+ def title(self):
+ """Title of WindowContainer."""
+ return self._title
+
+ def render(self, width):
+ """Render widgets to it's internal buffer.
+
+ :param width: the maximum width the item can use
+ :type width: int
+
+ :return: nothing
+ """
+ super().render(width)
+
+ # set cursor position to top-left corner
+ self.set_cursor_position(0, 0)
+
+ if self._title:
+ self._draw_title_and_separator(width)
+
+ for item in self._items:
+ widget = item.widget
+ widget.render(width)
+ self.draw(widget)
+
+ def _draw_title_and_separator(self, width):
+ title_widget = TextWidget(self._title)
+ sep = SeparatorWidget()
+
+ title_widget.render(width)
+ sep.render(width)
+
+ self.draw(title_widget)
+ self.draw(sep)
+
+
+class ListRowContainer(Container):
+ """Place widgets in rows automatically.
+
+ Compared to the ColumnWidget this is able to handle word wrapping correctly.
+
+ There is numbering N) automatically for all items. To disable this feature call
+ `self.key_pattern = None`. If you want other numbering then look on `KeyPattern` class.
+
+ Widgets will be placed based on the number of columns in the following way:
+
+ 1) w1 2) w2 3) w3
+ 4) w4 5) w5 6) w6
+ ....
+ """
+
+ def __init__(self, columns, items=None, columns_width=None, spacing=3, numbering=True):
+ """Create ListWidget with specific number of columns.
+
+ :param columns: How many columns we want.
+ :type columns: int, bigger than 0
+
+ :param items: List of items for positioning in this Container. Callback can't be
+ specified this way.
+ :type items: List of items for rendering.
+
+ :param columns_width: Width of every column. If nothing specified the maximum width
+ will be distributed to columns.
+ :type columns_width: int or None
+
+ :param spacing: Set the spacing between columns.
+ :type spacing: int
+
+ :param numbering: Enable/disable automatic numbering (labels) for items.
+ Enabled by default (True).
+ :type numbering: bool
+ """
+ super().__init__(items, numbering)
+ self._columns = columns
+ self._columns_width = columns_width
+ self._spacing = spacing
+ self._numbering_widgets = []
+
+ def render(self, width):
+ """Render widgets to it's internal buffer.
+
+ :param width: the maximum width the item can use
+ :type width: int
+
+ :return: nothing
+ """
+ super().render(width)
+
+ if self._columns_width is None:
+ spaces_between_columns = self._columns - 1
+ sum_spacing = spaces_between_columns * self._spacing
+ self._columns_width = int((width - sum_spacing) / self._columns)
+
+ ordered_map = self._get_ordered_map()
+ lines_per_rows = self._lines_per_every_row(ordered_map)
+
+ # the leftmost empty column
+ col_pos = 0
+
+ for col in ordered_map:
+ row_pos = 0
+
+ # render and draw contents of column
+ for row_id, item_id in enumerate(col):
+ container = self._items[item_id]
+ widget = container.widget
+
+ # set cursor to first line and leftmost empty column
+ self.set_cursor_position(row_pos, col_pos)
+
+ if self._key_pattern is not None:
+ number_widget = self._numbering_widgets[item_id]
+ widget_width = len(number_widget.text)
+ self.draw(number_widget)
+ self.set_cursor_position(row_pos, col_pos + widget_width)
+
+ self.draw(widget, block=True)
+ row_pos = row_pos + lines_per_rows[row_id]
+
+ # recompute the leftmost empty column
+ col_pos = max((col_pos + self._columns_width), self.width) + self._spacing
+
+ def _lines_per_every_row(self, items):
+ self._render_all_items()
+ # call `self._render_and_calculate_lines_per_rows()` method instead
+ lines_per_row = []
+
+ # go through all items and find how many lines we need for each row
+ # printed (because of wrapping)
+ for column_items in items:
+ for row_id, item_id in enumerate(column_items):
+ item = self._items[item_id]
+ if len(lines_per_row) <= row_id:
+ lines_per_row.append(0)
+
+ lines_per_row[row_id] = max(lines_per_row[row_id], len(item.widget.get_lines()))
+
+ return lines_per_row
+
+ def _render_all_items(self):
+ for item_id, item in enumerate(self._items):
+ item_width = self._columns_width
+
+ if item_width <= 0:
+ raise ValueError("Widget can't be rendered! Columns width is too small.")
+
+ if self._key_pattern:
+ number_widget = self.create_number_label(item_id)
+ # render numbers before widgets
+ number_width = len(number_widget.text)
+ number_widget.render(number_width)
+ self._numbering_widgets.append(number_widget)
+ # reduce the size of widget because of the number
+ item_width -= number_width
+
+ if item_width <= 0:
+ raise ValueError("Widget can't be rendered with numbering on! "
+ "Increase column width or disable numbering.")
+
+ item.widget.render(item_width)
+
+ def _get_ordered_map(self):
+ """Return list of identifiers (index) to the original item list.
+
+ .. NOTE: Use of ``self._prepare_list()` is encouraged to create output list and
+ just fill up this list.
+ """
+ # create list of columns (lists)
+ ordering_map = self._prepare_list()
+
+ for item_id in range(self.size):
+ ordering_map[item_id % self._columns].append(item_id)
+
+ return ordering_map
+
+ def _prepare_list(self):
+ """Prepare list for items ordering to rows and columns.
+
+ List will be prepared as ([column 1], [column 2], ...)
+ """
+ return list(map(lambda x: [], range(0, self._columns)))
+
+
+class ListColumnContainer(ListRowContainer):
+ """Place widgets in columns automatically.
+
+ Compared to the ColumnWidget this is able to handle word wrapping correctly.
+
+ There is numbering N) automatically for all items. To disable this feature call
+ `self.key_pattern = None`. If you want other numbering then look on `KeyPattern` class.
+
+ Widgets will be placed based on the number of columns in the following way:
+
+ 1) w1 4) w4 7) w7
+ 2) w2 5) w5 8) w8
+ 3) w3 6) w6 9) w9
+ """
+
+ def _get_ordered_map(self):
+ ordering_map = self._prepare_list()
+ items_in_column = ceil(len(self._items) / self._columns)
+
+ for item_id in range(self.size):
+ col_position = int(item_id // items_in_column)
+ ordering_map[col_position].append(item_id)
+
+ return ordering_map
+
+
+class KeyPattern():
+ """Pattern for automatic key printing before items."""
+
+ def __init__(self, pattern="{:d}) ", offset=1):
+ """Create the pattern class.
+
+ For enabling greater functionality than python 3 format is able to do, feel free to
+ override this class and use your subclass instead.
+
+ :param pattern: Set pattern which will be called for every item.
+ :type pattern: Strings format method.
+ See https://docs.python.org/3.3/library/string.html#format-string-syntax.
+
+ :param offset: Set the offset for numbering items. Default is 1 to start indexing
+ naturally for user.
+ :type offset: int
+ """
+ self._pattern = pattern
+ self._offset = offset
+
+ def get_widget_label(self, item_id):
+ """Get widget identifier for user input description.
+
+ It should be something similar to the pattern.
+
+ :param item_id: Position of the widget in the list.
+ :type item_id: int starts from 0.
+ """
+ return self._pattern.format(item_id + self._offset)
+
+ def translate_input_to_widget_id(self, user_input):
+ """Get id of the widget from the user input.
+
+ This is reverse translation to `self.get_widget_identifier()`.
+
+ :param user_input: Input from user:
+ :type user_input: str
+
+ :return: ID of the widget in the list or None if the input can't be translated.
+ :rtype: int or None
+ """
+ try:
+ return int(user_input) - 1
+ except ValueError:
+ log.debug("No callback registered for user input %s", user_input)
+ return None
+
+
+class ContainerItem():
+ """Item used inside of containers to store widgets callbacks and data.
+
+ Internal representation for Containers. Do not use this class directly.
+ """
+
+ def __init__(self, widget, callback=None, data=None):
+ """Construct WidgetContainer.
+
+ :param widget: Any item from `simpleline.render.widgets` or `Container`.
+ :type widget: Class subclassing the `simpleline.render.widgets.Widget` class
+ or `simpleline.render.containers.Container`.
+
+ :param callback: This callback will be called as reaction on user input.
+ :type callback: Function with one data parameter: `def func(data):`.
+
+ :param data: Params which will be passed to callback.
+ :type data: Anything.
+ """
+ self.widget = widget
+ self.callback = callback
+ self.data = data
diff --git a/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/render/prompt.py b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/render/prompt.py
new file mode 100644
index 0000000..4c99e35
--- /dev/null
+++ b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/render/prompt.py
@@ -0,0 +1,154 @@
+# Class for the Anaconda TUI prompt.
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+# Author(s): Vendula Poncova
+#
+import logging
+
+from simpleline.utils.i18n import N_, _
+
+log = logging.getLogger("simpleline")
+
+
+class Prompt():
+ """Class to create a prompt message with options."""
+ # Default message of the prompt
+ DEFAULT_MESSAGE = N_("Please make a selection from the above")
+
+ # String to use in a prompt when we want users to press the key ENTER.
+ ENTER = N_("ENTER")
+
+ # TRANSLATORS: 'q' to quit
+ QUIT_DESCRIPTION = N_("to quit")
+ QUIT = 'q'
+
+ # TRANSLATORS:'c' to continue
+ CONTINUE_DESCRIPTION = N_("to continue")
+ CONTINUE = 'c'
+
+ # TRANSLATORS:'r' to refresh
+ REFRESH_DESCRIPTION = N_("to refresh")
+ REFRESH = 'r'
+
+ # TRANSLATORS:'h' to help
+ HELP_DESCRIPTION = N_("to help")
+ HELP = 'h'
+
+ def __init__(self, message=DEFAULT_MESSAGE):
+ """
+ :param message: the message of the prompt
+ :type message: str|None
+ """
+ self.message = message
+ self.options = dict()
+
+ def set_message(self, message):
+ """Set the prompt message.
+
+ :param message: the message of the prompt
+ :type message: str|None
+ """
+ self.message = message
+
+ def add_option(self, key, description):
+ """Add an option to the prompt.
+ Causes a warning if the option already exists.
+
+ :param key: the key for choosing the option
+ :type key: str
+
+ :param description: the description of the option
+ :type description: str
+ """
+ if key in self.options:
+ log.warning("The option '%s' does already exist in '%s'.", key, self)
+
+ self.options[key] = description
+
+ def update_option(self, key, description):
+ """Update an option in the prompt.
+ Causes a warning if the option does not exist.
+
+ :param key: the key for choosing the option
+ :type key: str
+
+ :param description: the description of the option
+ :type description: str
+ """
+ if key not in self.options:
+ log.warning("The option '%s' does not exist in '%s'.", key, self)
+
+ self.options[key] = description
+
+ def add_refresh_option(self, description=REFRESH_DESCRIPTION):
+ """Add the option to refresh."""
+ if Prompt.REFRESH in self.options:
+ self.update_option(Prompt.REFRESH, description)
+ else:
+ self.add_option(Prompt.REFRESH, description)
+
+ def add_continue_option(self, description=CONTINUE_DESCRIPTION):
+ """Add the option to continue."""
+ if Prompt.CONTINUE in self.options:
+ self.update_option(Prompt.CONTINUE, description)
+ else:
+ self.add_option(Prompt.CONTINUE, description)
+
+ def add_quit_option(self, description=QUIT_DESCRIPTION):
+ """Add the option to quit."""
+ if Prompt.QUIT in self.options:
+ self.update_option(Prompt.QUIT, description)
+ else:
+ self.add_option(Prompt.QUIT, description)
+
+ def add_help_option(self, description=HELP_DESCRIPTION):
+ """Add the option to help."""
+ if Prompt.HELP in self.options:
+ self.update_option(Prompt.HELP, description)
+ else:
+ self.add_option(Prompt.HELP, description)
+
+ def remove_option(self, key):
+ """Remove an option with the given key.
+
+ :param key: the key of the option
+ :type key: str
+
+ :return: the removed option
+ :rtype: str|None
+ """
+ return self.options.pop(key, None)
+
+ def __str__(self):
+ """Return the string representation of the prompt."""
+ if not self.message and not self.options:
+ return ""
+
+ parts = []
+
+ if self.message:
+ parts.append(_(self.message))
+
+ if self.options:
+ opt_list = ["'%s' %s" % (key, _(self.options[key]))
+ for key in sorted(self.options.keys())]
+ opt_str = "[%s]" % ", ".join(opt_list)
+ parts.append(opt_str)
+
+ return " ".join(parts) + ": "
diff --git a/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/render/screen/__init__.py b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/render/screen/__init__.py
new file mode 100644
index 0000000..6918b28
--- /dev/null
+++ b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/render/screen/__init__.py
@@ -0,0 +1,313 @@
+# Base class for text window screens.
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+# Author(s): Jiri Konecny
+#
+
+from enum import Enum
+
+from simpleline import App
+from simpleline.render.containers import WindowContainer
+from simpleline.render.prompt import Prompt
+from simpleline.render.screen.signal_handler import SignalHandler
+from simpleline.render.screen.input_manager import InputManager
+from simpleline.utils.i18n import _
+
+__all__ = ["UIScreen", "InputState"]
+
+
+class UIScreen(SignalHandler):
+ """Base class representing one TUI Screen.
+
+ Shares some API with anaconda's GUI to make it easy for devs to create similar UI
+ with the familiar API.
+ """
+
+ def __init__(self, title=None, screen_height=30):
+ """ Constructor of the TUI screen.
+
+ :param title: Title line of the screen.
+ :type title: str
+
+ :param screen_height: height of the screen (useful for printing long widgets)
+ :type screen_height: int (the value must be bigger than 4)
+ """
+ self._title = title
+ self._screen_height = screen_height
+ self._screen_ready = False
+
+ # ask for password
+ self._hide_user_input = False
+ self._password_func = None
+
+ # do not print separator for this screen
+ self._no_separator = False
+
+ # list that holds the content to be printed out
+ self._window = WindowContainer(self.title)
+
+ # should the input be required after draw
+ self._input_required = True
+
+ # index of the page (subset of screen) shown during show_all
+ # indexing starts with 0
+ self._page = 0
+
+ self._input_manager = InputManager(ui_screen=self)
+
+ def __str__(self):
+ """For easier logging."""
+ return self.__class__.__name__
+
+ @property
+ def title(self):
+ """Screen title."""
+ return self._title
+
+ @title.setter
+ def title(self, title):
+ """Set screen title.
+
+ Set `None` to remove title.
+ """
+ self._title = title
+
+ @property
+ def password_func(self):
+ """Get password function.
+
+ This is function with one argument to get password from command line.
+ """
+ return self._password_func
+
+ @password_func.setter
+ def password_func(self, value):
+ """Set password function.
+
+ :param value: Function to get password from a command line.
+ :type value: Function with one argument which is text representation of prompt.
+ """
+ self._password_func = value
+
+ @property
+ def screen_ready(self):
+ """This screen is ready for use."""
+ return self._screen_ready
+
+ @screen_ready.setter
+ def screen_ready(self, screen_ready):
+ """Set ready status for this screen."""
+ self._screen_ready = screen_ready
+
+ @property
+ def input_required(self):
+ """Return if the screen requires input."""
+ return self._input_required
+
+ @input_required.setter
+ def input_required(self, input_required):
+ """Set if the screen should require input."""
+ self._input_required = input_required
+
+ @property
+ def no_separator(self):
+ """Should we print separator for this screen?
+
+ :returns: True to print separator before this screen (default).
+ False do not print separator.
+ """
+ return self._no_separator
+
+ @no_separator.setter
+ def no_separator(self, no_separator):
+ """Print or do not print separator.
+
+ :param no_separator: Specify if the separator should be printed.
+ :type no_separator: bool (default: False).
+ """
+ self._no_separator = no_separator
+
+ @property
+ def hide_user_input(self):
+ """Hide typed user input.
+
+ This is main solution how to ask for password.
+
+ :returns: True if user input should be hidden.
+ False otherwise (default).
+ """
+ return self._hide_user_input
+
+ @hide_user_input.setter
+ def hide_user_input(self, hide_input):
+ """Should be the user input hidden.
+
+ :param hide_input: True if user input should be hidden.
+ False if not (default).
+ :type hide_input: bool (default: False).
+ """
+ self._hide_user_input = hide_input
+
+ @property
+ def window(self):
+ """Return WindowContainer instance."""
+ return self._window
+
+ @window.setter
+ def window(self, window):
+ """Set base WindowContainer instance.
+
+ :param window: Base window container containing other widgets and containers.
+ :type window: Instance of `simpleline.render.containers.WindowContainer` class.
+ """
+ self._window = window
+
+ def get_user_input(self, message, hidden=False):
+ """Get immediately input from the user.
+
+ Use this with cautious. Never call this in middle of rendering or when other
+ input is already waiting. It is recommended to use `self.input_required` instead.
+
+ :param message: Message prompt for the user.
+ :type message: str
+
+ :param hidden: Do not echo user input (password typing).
+ :type hidden: bool
+ """
+ return self._input_manager.get_input_blocking(message, hidden)
+
+ def setup(self, args):
+ """Do additional setup right before this screen is used.
+
+ It is mandatory to call this ancestor method in the child class to set ready status.
+
+ :param args: arguments for the setup
+ :type args: array of values
+ :return: whether this screen should be scheduled or not
+ :rtype: bool
+ """
+ self._screen_ready = True
+ App.get_event_loop().register_signal_source(self)
+ return True
+
+ def refresh(self, args=None):
+ """Method which prepares the content desired on the screen to `self.window`.
+
+ :param args: optional argument passed from switch_screen calls
+ :type args: anything
+ """
+ self.window = WindowContainer(self._title)
+
+ def _print_widget(self, widget):
+ """Prints a widget with user interaction (when needed).
+
+ Could be longer than the screen height.
+
+ :param widget: widget to print
+ :type widget: Widget instance
+ """
+ # TODO: Work even for lower screen_height than 4
+ pos = 0
+ lines = widget.get_lines()
+ num_lines = len(lines)
+
+ if num_lines == 0:
+ return
+
+ prompt_height = 2
+ real_screen_height = self._screen_height - prompt_height
+
+ if num_lines < real_screen_height:
+ # widget plus prompt are shorter than screen height, just print the widget
+ print(u"\n".join(lines))
+ return
+
+ # long widget, print it in steps and prompt user to continue
+ last_line = num_lines - 1
+ while pos <= last_line:
+ if pos + real_screen_height > last_line:
+ # enough space to print the rest of the widget plus regular
+ # prompt (2 lines)
+ for line in lines[pos:]:
+ print(line)
+ pos += self._screen_height - 1
+ else:
+ # print part with a prompt to continue
+ for line in lines[pos:(pos + real_screen_height)]:
+ print(line)
+ custom_prompt = Prompt(_("\nPress %s to continue") % Prompt.ENTER)
+ self._ask_user_input_blocking(custom_prompt)
+ pos += real_screen_height
+
+ def _ask_user_input_blocking(self, prompt):
+ return self._input_manager.get_input_blocking(prompt, False)
+
+ def show_all(self):
+ """Print WindowContainer in `self.window` with all its content."""
+ self.window.render(App.get_configuration().width)
+ self._print_widget(self.window)
+
+ def input(self, args, key):
+ """Method called to process input. If the input is not handled here, return it.
+
+ :param key: input string to process
+ :type key: str
+ :param args: optional argument passed from switch_screen calls
+ :type args: anything
+ :return: return `simpleline.render.InputState.PROCESSED` if key was handled,
+ `simpleline.render.InputState.DISCARDED` if the screen should not process input
+ on the scheduler and key if you want it to.
+ :rtype: `simpleline.render.InputState` enum | str
+ """
+ return key
+
+ def get_input_with_error_check(self, args):
+ """Get user input and redraw if user add too many invalid inputs.
+
+ This method should be used only by ScreenScheduler.
+
+ :param args: Arguments passed in when scheduling this screen.
+ :type args: Anything.
+ """
+ self._input_manager.get_input(args=args)
+
+ def prompt(self, args=None):
+ """Return the text to be shown as prompt or handle the prompt and return None.
+
+ :param args: optional argument passed from switch_screen calls
+ :type args: anything
+ :return: returns an instance of Prompt with text to be shown next to the prompt
+ for input or None to skip further input processing
+ :rtype: Prompt instance|None
+ """
+ prompt = Prompt()
+ prompt.add_refresh_option()
+ prompt.add_continue_option()
+ prompt.add_quit_option()
+ return prompt
+
+ def closed(self):
+ """Callback when this screen is closed."""
+
+
+class InputState(Enum):
+ PROCESSED = 1
+ PROCESSED_AND_REDRAW = 2
+ PROCESSED_AND_CLOSE = 3
+ DISCARDED = 0
diff --git a/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/render/screen/__pycache__/__init__.cpython-314.pyc b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/render/screen/__pycache__/__init__.cpython-314.pyc
new file mode 100644
index 0000000..93faa7d
Binary files /dev/null and b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/render/screen/__pycache__/__init__.cpython-314.pyc differ
diff --git a/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/render/screen/__pycache__/input_manager.cpython-314.pyc b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/render/screen/__pycache__/input_manager.cpython-314.pyc
new file mode 100644
index 0000000..5b75040
Binary files /dev/null and b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/render/screen/__pycache__/input_manager.cpython-314.pyc differ
diff --git a/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/render/screen/__pycache__/signal_handler.cpython-314.pyc b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/render/screen/__pycache__/signal_handler.cpython-314.pyc
new file mode 100644
index 0000000..e39ddaf
Binary files /dev/null and b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/render/screen/__pycache__/signal_handler.cpython-314.pyc differ
diff --git a/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/render/screen/input_manager.py b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/render/screen/input_manager.py
new file mode 100644
index 0000000..dc0c220
--- /dev/null
+++ b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/render/screen/input_manager.py
@@ -0,0 +1,203 @@
+# Class for managing input and output for application.
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+# Author(s): Jiri Konecny
+#
+
+from enum import Enum
+
+from simpleline import App
+from simpleline.event_loop import ExitMainLoop
+from simpleline.event_loop.signals import ExceptionSignal
+from simpleline.render.prompt import Prompt
+from simpleline.input import InputHandler, PasswordInputHandler
+
+from simpleline.logging import get_simpleline_logger
+
+log = get_simpleline_logger()
+
+
+class InputManager():
+
+ def __init__(self, ui_screen):
+ """Processor for user input.
+
+ This class is mainly helper class for ScreenScheduler.
+
+ :param ui_screen: Screen associated with this input manager.
+ :type ui_screen: The `simpleline.render.screen.UIScreen` based instance.
+ """
+ super().__init__()
+ self._ui_screen = ui_screen
+ self._input_error_counter = 0
+ self._input_error_threshold = 5
+ self._input_args = None
+
+ @property
+ def input_error_counter(self):
+ """Return how many times the user provided bad input."""
+ return self._input_error_counter
+
+ @property
+ def input_error_threshold_exceeded(self):
+ """Did the error counter pass the threshold?
+
+ The screen should be redraw.
+ """
+ errors = self._input_error_counter % self._input_error_threshold
+ return errors == 0
+
+ def get_input_blocking(self, message, hidden):
+ """Get blocking input from the user.
+
+ :param message: Message prompt for the user.
+ :type message: str
+
+ :param hidden: Do not echo user input (password typing).
+ :type hidden: bool
+ """
+ if hidden:
+ handler = PasswordInputHandler(source=self)
+ if self._ui_screen.password_func:
+ handler.set_pass_func(self._ui_screen.password_func)
+ else:
+ handler = InputHandler(source=self)
+
+ handler.get_input(message)
+ handler.wait_on_input()
+ return handler.value
+
+ def get_input(self, args=None):
+ """Get input from user.
+
+ :param args: Arguments passed in when UIScreen was scheduled.
+ :type args: Anything.
+ """
+ prompt = self._ui_screen.prompt(args)
+ if not self._is_input_expected(prompt):
+ return
+
+ self._input_args = args
+
+ if not self._ui_screen.hide_user_input:
+ handler = InputHandler(source=self._ui_screen)
+ else:
+ handler = PasswordInputHandler(source=self._ui_screen)
+ if self._ui_screen.password_func:
+ handler.set_pass_func(self._ui_screen.password_func)
+
+ handler.set_callback(self.process_input)
+ handler.get_input(prompt)
+
+ def _is_input_expected(self, prompt):
+ """Check if user handled input processing some other way.
+
+ Do nothing if user did handled user input.
+
+ :returns: True if prompt is set and we can use it to get user input.
+ False if prompt is not available, which means that user handled input on their
+ own.
+ """
+ # None means prompt handled the input by itself -> continue
+ if prompt is None:
+ self._input_error_counter = 0
+ return False
+
+ return True
+
+ def process_input(self, user_input):
+ """Process input from the screens.
+
+ :param user_input: User input string.
+ :type user_input: String.
+
+ :raises: ExitMainLoop or any other kind of exception from screen processing.
+ """
+ # process the input, if it wasn't processed (valid)
+ # increment the error counter
+ try:
+ result = self._process_input(user_input)
+ except ExitMainLoop: # pylint: disable=try-except-raise
+ raise
+ except Exception: # pylint: disable=broad-except
+ App.get_event_loop().enqueue_signal(ExceptionSignal(self))
+ return
+
+ if result.was_successful():
+ self._input_error_counter = 0
+ else:
+ self._input_error_counter += 1
+
+ App.get_scheduler().process_input_result(result, self.input_error_threshold_exceeded)
+
+ def _process_input(self, key):
+ """Method called internally to process unhandled input key presses.
+
+ :param key: The string entered by user.
+ :type key: String.
+
+ :return: Return state result object.
+ :rtype: `simpleline.render.in_out_manager.UserInputResult` class.
+
+ :raises: Anything the Screen can raise in the input processing.
+ """
+ from simpleline.render.screen import InputState # pylint: disable=import-outside-toplevel
+ # delegate the handling to active screen first
+ key = self._ui_screen.input(self._input_args, key)
+ if key == InputState.PROCESSED:
+ return UserInputAction.NOOP
+
+ if key == InputState.PROCESSED_AND_REDRAW:
+ return UserInputAction.REDRAW
+
+ if key == InputState.PROCESSED_AND_CLOSE:
+ return UserInputAction.CLOSE
+
+ if key == InputState.DISCARDED:
+ return UserInputAction.INPUT_ERROR
+
+ # global refresh command
+ if key == Prompt.REFRESH:
+ return UserInputAction.REDRAW
+
+ # global close command
+ if key == Prompt.CONTINUE:
+ return UserInputAction.CLOSE
+
+ # global quit command
+ if key == Prompt.QUIT:
+ return UserInputAction.QUIT
+
+ if key is None:
+ log.warning("Returned key from screen is None. "
+ "This could be missing return in a screen input method?")
+
+ return UserInputAction.INPUT_ERROR
+
+
+class UserInputAction(Enum):
+ """Store user input result."""
+ INPUT_ERROR = -1
+ NOOP = 0
+ REDRAW = 5
+ CLOSE = 6
+ QUIT = 7
+
+ def was_successful(self):
+ return self != UserInputAction.INPUT_ERROR
diff --git a/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/render/screen/signal_handler.py b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/render/screen/signal_handler.py
new file mode 100644
index 0000000..e5e1eb5
--- /dev/null
+++ b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/render/screen/signal_handler.py
@@ -0,0 +1,94 @@
+# Signal handler is giving ability connect and emit to all widgets.
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+# Author(s): Jiri Konecny
+#
+
+from simpleline import App
+from simpleline.event_loop.signals import RenderScreenSignal, CloseScreenSignal
+
+
+class SignalHandler():
+ """Provides methods for handling signals anc callbacks.
+
+ Provides main methods:
+ `connect()` -- connect this widget on given signal
+ `create_signal()` -- create signal class which can be used in the emit method
+ `emit()` -- emit signal given signal
+ """
+
+ def connect(self, signal, callback, data=None):
+ """Connect this class method with given signal.
+
+ :param signal: signal class which you want to connect
+ :type signal: class based on `simpleline.event_loop.AbstractSignal`
+
+ :param callback: the callback function
+ :type callback: func(event_message, data)
+
+ :param data: Data you want to pass to the callback
+ :type data: Anything
+ """
+ App.get_event_loop().register_signal_handler(signal, callback, data)
+
+ def create_signal(self, signal_class, priority=0):
+ """Create signal instance usable in the emit method.
+
+ :param signal_class: signal you want to use
+ :type signal_class: class based on `simpleline.event_loop.AbstractSignal`
+
+ :param priority: priority of the signal; please look on the
+ `simpleline.event_loop.AbstractSignal.priority` for further info
+ :type priority: int
+ """
+ return signal_class(self, priority)
+
+ def emit(self, signal):
+ """Emit the signal.
+
+ This will add `signal` to the event loop.
+
+ :param signal: signal to emit
+ :type signal: instance of class based on `simpleline.event_loop.AbstractSignal`
+ """
+ App.get_event_loop().enqueue_signal(signal)
+
+ def create_and_emit(self, signal):
+ """Create the signal and emit it.
+
+ This is basically shortcut for calling `self.create_signal` and `self.emit`.
+ """
+ created_signal = self.create_signal(signal)
+ self.emit(created_signal)
+
+ def redraw(self):
+ """Emit signal to initiate draw.
+
+ Add RenderScreenSignal to the event loop.
+ """
+ signal = self.create_signal(RenderScreenSignal)
+ App.get_event_loop().enqueue_signal(signal)
+
+ def close(self):
+ """Emit signal to close this screen.
+
+ Add CloseScreenSignal to the event loop.
+ """
+ signal = self.create_signal(CloseScreenSignal)
+ App.get_event_loop().enqueue_signal(signal)
diff --git a/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/render/screen_handler.py b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/render/screen_handler.py
new file mode 100644
index 0000000..19655f7
--- /dev/null
+++ b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/render/screen_handler.py
@@ -0,0 +1,58 @@
+# Serves shortcuts for easy screen scheduling.
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+# Author(s): Jiri Konecny
+#
+
+from simpleline import App
+
+
+class ScreenHandler():
+
+ @classmethod
+ def schedule_screen(cls, ui_screen, args=None):
+ """Schedule screen to the active scheduler.
+
+ See: `simpleline.render.screen_scheduler.schedule_screen()`.
+ """
+ App.get_scheduler().schedule_screen(ui_screen=ui_screen, args=args)
+
+ @classmethod
+ def replace_screen(cls, ui_screen, args=None):
+ """Schedule screen to the active scheduler.
+
+ See: `simpleline.render.screen_scheduler.replace_screen()`.
+ """
+ App.get_scheduler().replace_screen(ui_screen=ui_screen, args=args)
+
+ @classmethod
+ def push_screen(cls, ui_screen, args=None):
+ """Schedule screen to the active scheduler.
+
+ See: `simpleline.render.screen_scheduler.push_screen()`.
+ """
+ App.get_scheduler().push_screen(ui_screen=ui_screen, args=args)
+
+ @classmethod
+ def push_screen_modal(cls, ui_screen, args=None):
+ """Schedule screen to the active scheduler.
+
+ See: `simpleline.render.screen_scheduler.push_screen_modal()`.
+ """
+ App.get_scheduler().push_screen_modal(ui_screen=ui_screen, args=args)
diff --git a/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/render/screen_scheduler.py b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/render/screen_scheduler.py
new file mode 100644
index 0000000..cfc4cbf
--- /dev/null
+++ b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/render/screen_scheduler.py
@@ -0,0 +1,311 @@
+# Class handling rendering of the screens to console.
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+# Author(s): Jiri Konecny
+#
+
+import threading
+
+
+from simpleline import App
+from simpleline.event_loop import ExitMainLoop
+from simpleline.event_loop.signals import ExceptionSignal, RenderScreenSignal, CloseScreenSignal
+from simpleline.render import RenderUnexpectedError
+from simpleline.render.screen.input_manager import UserInputAction
+from simpleline.render.screen_stack import ScreenStack, ScreenData, ScreenStackEmptyException
+
+from simpleline.logging import get_simpleline_logger
+
+log = get_simpleline_logger()
+
+RAW_INPUT_LOCK = threading.Lock()
+
+
+__all__ = ["ScreenScheduler"]
+
+
+class ScreenScheduler():
+
+ def __init__(self, event_loop, scheduler_stack=None):
+ """Constructor where you can pass your own scheduler stack.
+
+ The ScreenStack will be used automatically if scheduler stack will be None.
+
+ :param event_loop: Event loop used for the scheduler.
+ :type event_loop: Class based on `simpleline.event_loop.AbstractEventLoop`.
+ :param scheduler_stack: Use custom scheduler stack if you need to.
+ :type scheduler_stack: `simpleline.screen_stack.ScreenStack` based class.
+ """
+ self._quit_screen = None
+ self._event_loop = event_loop
+
+ if scheduler_stack:
+ self._screen_stack = scheduler_stack
+ else:
+ self._screen_stack = ScreenStack()
+ self._register_handlers()
+
+ self._first_screen_scheduled = False
+
+ @staticmethod
+ def _spacer():
+ return "\n".join(2 * [App.get_configuration().width * "="])
+
+ def _register_handlers(self):
+ self._event_loop.register_signal_handler(RenderScreenSignal, self._process_screen_callback)
+ self._event_loop.register_signal_handler(CloseScreenSignal, self._close_screen_callback)
+
+ @property
+ def quit_screen(self):
+ """Return quit UIScreen."""
+ return self._quit_screen
+
+ @quit_screen.setter
+ def quit_screen(self, quit_screen):
+ """Set the UIScreen based instance which will be showed before the Application will quit.
+
+ You can also use `simpleline.render.adv_widgets.YesNoDialog` or `UIScreen` based class
+ with the `answer` property. Without the `answer` property the application will always
+ close.
+ """
+ self._quit_screen = quit_screen
+
+ @property
+ def nothing_to_render(self):
+ """Is something for rendering in the scheduler stack?
+
+ :return: True if the rendering stack is empty
+ :rtype: bool
+ """
+ return self._screen_stack.empty()
+
+ def dump_stack(self):
+ """Get string representation of actual screen stack."""
+ return self._screen_stack.dump_stack()
+
+ def schedule_screen(self, ui_screen, args=None):
+ """Add screen to the bottom of the stack.
+
+ This is mostly useful at the beginning to prepare the first screen hierarchy to display.
+
+ :param ui_screen: screen to show
+ :type ui_screen: UIScreen instance
+ :param args: optional argument, please see switch_screen for details
+ :type args: anything
+ """
+ log.debug("Scheduling screen %s", ui_screen)
+ screen = ScreenData(ui_screen, args)
+ self._screen_stack.add_first(screen)
+ self._redraw_on_first_scheduled_screen()
+
+ def _redraw_on_first_scheduled_screen(self):
+ if not self._first_screen_scheduled:
+ self.redraw()
+ self._first_screen_scheduled = True
+
+ def replace_screen(self, ui_screen, args=None):
+ """Schedules a screen to replace the current one.
+
+ :param ui_screen: screen to show
+ :type ui_screen: instance of UIScreen
+ :param args: optional argument to pass to ui's refresh and setup methods
+ (can be used to select what item should be displayed or so)
+ :type args: anything
+ """
+ log.debug("Replacing screen %s", ui_screen)
+ try:
+ execute_new_loop = self._screen_stack.pop().execute_new_loop
+ except ScreenStackEmptyException as e:
+ raise ScreenStackEmptyException("Switch screen is not possible when there is no "
+ "screen scheduled!") from e
+
+ # we have to keep the old_loop value so we stop
+ # dialog's mainloop if it ever uses switch_screen
+ screen = ScreenData(ui_screen, args, execute_new_loop)
+ self._screen_stack.append(screen)
+ self.redraw()
+
+ def push_screen(self, ui_screen, args=None):
+ """Schedules a screen to show, but keeps the current one in stack to
+ return to, when the new one is closed.
+
+ :param ui_screen: screen to show
+ :type ui_screen: UIScreen instance
+ :param args: optional argument
+ :type args: anything
+ """
+ log.debug("Pushing screen %s to stack", ui_screen)
+ screen = ScreenData(ui_screen, args, False)
+ self._screen_stack.append(screen)
+ self.redraw()
+
+ def push_screen_modal(self, ui_screen, args=None):
+ """Starts a new screen right away, so the caller can collect data back.
+
+ When the new screen is closed, the caller is redisplayed.
+
+ This method does not return until the new screen is closed.
+
+ :param ui_screen: screen to show
+ :type ui_screen: UIScreen instance
+ :param args: optional argument, please see switch_screen for details
+ :type args: anything
+ """
+ log.debug("Pushing modal screen %s to stack", ui_screen)
+ screen = ScreenData(ui_screen, args, True)
+ self._screen_stack.append(screen)
+ # only new events will be processed now
+ # the old one will wait after this event loop will be closed
+ self._event_loop.execute_new_loop(RenderScreenSignal(self))
+
+ def _close_screen_callback(self, signal, data):
+ self.close_screen(signal.source)
+
+ def close_screen(self, closed_from=None):
+ """Close the currently displayed screen and exit it's main loop if necessary.
+
+ Next screen from the stack is then displayed.
+ """
+ screen = self._screen_stack.pop()
+ log.debug("Closing screen %s from %s", screen, closed_from)
+
+ # User can react when screen is closing
+ screen.ui_screen.closed()
+
+ if closed_from is not None and closed_from is not screen.ui_screen:
+ raise RenderUnexpectedError("You are trying to close screen %s from screen %s! "
+ "This is most probably not intentional." %
+ (closed_from, screen.ui_screen))
+
+ if screen.execute_new_loop:
+ self._event_loop.close_loop()
+
+ # redraw screen if there is what to redraw
+ # and if it is not modal screen (modal screen parent is blocked)
+ if not self._screen_stack.empty() and not screen.execute_new_loop:
+ self.redraw()
+
+ # we can't draw anything more. Kill the application.
+ if self._screen_stack.empty():
+ raise ExitMainLoop()
+
+ def redraw(self):
+ """Register rendering to the event loop for processing."""
+ self._event_loop.enqueue_signal(RenderScreenSignal(self))
+
+ def _process_screen_callback(self, signal, data):
+ self._process_screen()
+
+ def _process_screen(self):
+ """Process the current screen.
+
+ 1) It will call setup if the screen is not already set.
+ 2a) If setup was success then draw the screen.
+ 2b) If setup wasn't successful then pop the screen and try to process next in the stack.
+ Continue by (1).
+ 3)Ask for user input if requested.
+ """
+ top_screen = self._get_last_screen()
+
+ log.debug("Processing screen %s", top_screen)
+
+ # this screen is used first time (call setup() method)
+ if not top_screen.ui_screen.screen_ready:
+ if not top_screen.ui_screen.setup(top_screen.args):
+ # remove the screen and skip if setup went wrong
+ self._screen_stack.pop()
+ self.redraw()
+ log.warning("Screen %s setup wasn't successful", top_screen)
+ return
+
+ # get the widget tree from the screen and show it in the screen
+ try:
+ # refresh screen content
+ top_screen.ui_screen.refresh(top_screen.args)
+
+ # Screen was closed in the refresh method
+ if top_screen != self._get_last_screen():
+ return
+
+ # draw screen to the console
+ self._draw_screen(top_screen)
+
+ if top_screen.ui_screen.input_required:
+ log.debug("Input is required by %s screen", top_screen)
+ top_screen.ui_screen.get_input_with_error_check(top_screen.args)
+ except ExitMainLoop: # pylint: disable=try-except-raise
+ raise
+ except Exception: # pylint: disable=broad-except
+ self._event_loop.enqueue_signal(ExceptionSignal(self))
+ return
+
+ def _draw_screen(self, active_screen):
+ """Draws the current `active_screen`.
+
+ :param active_screen: Screen which should be draw to the console.
+ :type active_screen: Classed based on `simpleline.render.screen.UIScreen`.
+ """
+ # get the widget tree from the screen and show it in the screen
+ try:
+ if not active_screen.ui_screen.no_separator:
+ # separate the content on the screen from the stuff we are about to display now
+ print(self._spacer())
+
+ # print UIScreen content
+ active_screen.ui_screen.show_all()
+ except ExitMainLoop: # pylint: disable=try-except-raise
+ raise
+ except Exception: # pylint: disable=broad-except
+ self._event_loop.enqueue_signal(ExceptionSignal(self))
+
+ def _get_last_screen(self):
+ if self._screen_stack.empty():
+ raise ExitMainLoop()
+
+ return self._screen_stack.pop(False)
+
+ def process_input_result(self, input_result, should_redraw):
+ active_screen = self._get_last_screen()
+
+ if not input_result.was_successful():
+ if should_redraw:
+ self.redraw()
+ else:
+ log.debug("Input was not successful, ask for new input.")
+ active_screen.ui_screen.get_input_with_error_check(active_screen.args)
+ else:
+ if input_result == UserInputAction.NOOP:
+ return
+
+ if input_result == UserInputAction.REDRAW:
+ self.redraw()
+ elif input_result == UserInputAction.CLOSE:
+ self.close_screen()
+ elif input_result == UserInputAction.QUIT:
+ if self.quit_screen:
+ self.push_screen_modal(self.quit_screen)
+ try:
+ if self.quit_screen.answer is True:
+ raise ExitMainLoop()
+
+ self.redraw()
+ except AttributeError as e:
+ raise ExitMainLoop() from e
+ else:
+ raise ExitMainLoop()
diff --git a/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/render/screen_stack.py b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/render/screen_stack.py
new file mode 100644
index 0000000..9d0d7c2
--- /dev/null
+++ b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/render/screen_stack.py
@@ -0,0 +1,117 @@
+# Classes implementation for storing and manipulating Screen stack.
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+# Author(s): Jiri Konecny
+#
+
+
+from simpleline.errors import SimplelineError
+
+
+class ScreenStackException(SimplelineError):
+ """General screen stack exception."""
+
+
+class ScreenStackEmptyException(ScreenStackException):
+ """Screen stack exception when stack is empty."""
+
+
+class ScreenStack():
+ """Managing screen stack used in `ScreenScheduler`."""
+
+ def __init__(self):
+ self._screens = []
+
+ def empty(self):
+ """Test if screen stack is empty.
+
+ :return: True if empty.
+ :rtype: bool
+ """
+ return not self._screens
+
+ def size(self):
+ """Get size of the stack.
+
+ :return: Size of the stack.
+ """
+ return len(self._screens)
+
+ def append(self, screen):
+ """Add new screen to the top of the stack.
+
+ :param screen: Screen for the future rendering.
+ :type screen: Class based on `simpleline.render.ui_screen.UIScreen`.
+ """
+ self._screens.append(screen)
+
+ def pop(self, remove=True):
+ """Return top item from the stack.
+
+ :param remove: If True (default) also remove this items from the stack.
+ :return: The top screen on the stack.
+ """
+ try:
+ if remove:
+ return self._screens.pop()
+
+ return self._screens[-1]
+ except IndexError as e:
+ raise ScreenStackEmptyException(e) from e
+
+ def add_first(self, screen):
+ """Add `screen` to the bottom of the stack.
+
+ :param screen: Add the `screen` to the bottom of the stack.
+ :type screen: Class based on `simpleline.render.ui_screen.UIScreen`.
+ """
+ self._screens.insert(0, screen)
+
+ def dump_stack(self):
+ """Dump screen stack structure.
+
+ :returns: Screen stack representation.
+ :rtype: str
+ """
+ msg = '======= Screen stack =======\n'
+ msg += '----------- TOP ------------\n'
+
+ for screen in reversed(self._screens):
+ msg += str(screen)
+ msg += "\n"
+
+ msg += '============================\n'
+
+ return msg
+
+
+class ScreenData():
+ """Inner data class to store screen data."""
+
+ def __init__(self, ui_screen, args=None, execute_new_loop=False):
+ self.ui_screen = ui_screen
+ self.args = args
+ self.execute_new_loop = execute_new_loop
+
+ def __str__(self):
+ msg = self.__class__.__name__
+ msg += "("
+ msg += ",".join((str(self.ui_screen), str(self.args), str(self.execute_new_loop)))
+ msg += ")"
+ return msg
diff --git a/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/render/widgets.py b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/render/widgets.py
new file mode 100644
index 0000000..2f645a0
--- /dev/null
+++ b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/render/widgets.py
@@ -0,0 +1,495 @@
+# Widgets for Text UI framework.
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+
+
+import functools
+from textwrap import wrap
+from simpleline.utils.i18n import _
+from simpleline.utils import ensure_str
+
+__all__ = ["Widget", "TextWidget", "SeparatorWidget", "EntryWidget", "ColumnWidget",
+ "CheckboxWidget", "CenterWidget"]
+
+
+class Widget():
+
+ def __init__(self, max_width=None, default=None):
+ """Initializes base Widgets buffer.
+
+ This class can be subclassed to create customized widgets.
+
+ :param max_width: serves as a hint about screen size to write method with default arguments
+ :type max_width: int
+
+ :param default: string containing the default content to fill the buffer with
+ :type default: string
+ """
+ self._buffer = []
+ if default:
+ self._buffer = [[c for c in l] for l in default.split("\n")] # pylint: disable=unnecessary-comprehension
+ self._max_width = max_width
+ self._cursor = (0, 0) # row, col
+
+ @property
+ def height(self):
+ """The current height of the internal buffer."""
+ return len(self._buffer)
+
+ @property
+ def width(self):
+ """The current width of the internal buffer (id of the first empty column)."""
+ return functools.reduce(lambda acc, l: max(acc, len(l)), self._buffer, 0)
+
+ def clear(self):
+ """Clears this widgets buffer and resets cursor."""
+ self._buffer = list()
+ self._cursor = (0, 0)
+
+ @property
+ def content(self):
+ """Return a list (rows) of lists (columns) with one character elements."""
+ return self._buffer
+
+ def render(self, width):
+ """Redraw the widget's self._buffer.
+
+ :param width: the width of buffer requested by the caller
+ :type width: int
+
+ Commonly, call render of child widgets and then draw and write
+ methods to copy their contents to self._buffer.
+ """
+ self.clear()
+
+ def get_lines(self):
+ """Return lines to write out in order to show this widget.
+
+ :return: lines representing this widget
+ :rtype: list(str)
+ """
+ return [str(u"".join(line)) for line in self._buffer]
+
+ def set_cursor_position(self, row, col):
+ """Set cursor position.
+
+ :param row: row id, starts with 0 at the top of the screen
+ :type row: int
+
+ :param col: column id, starts with 0 on the left side of the screen
+ :type col: int
+ """
+ self._cursor = (row, col)
+
+ @property
+ def cursor(self):
+ return self._cursor
+
+ def set_end(self):
+ """Set the cursor to first column in new line at the end."""
+ self._cursor = (self.height, 0)
+
+ def draw(self, w, row=None, col=None, block=False):
+ """Copy w widget's content to this widget's buffer at row, col position.
+
+ :param w: widget to take content from
+ :type w: class Widget
+
+ :param row: row number to start at (default is at the cursor position)
+ :type row: int
+
+ :param col: column number to start at (default is at the cursor position)
+ :type col: int
+
+ :param block: when printing newline, start at column col (True) or at column 0 (False)
+ :type block: boolean
+ """
+ # if the starting row is not present, start at the cursor position
+ if row is None:
+ row = self._cursor[0]
+
+ # if the starting column is not present, start at the cursor position
+ if col is None:
+ col = self._cursor[1]
+
+ # fill up rows to accommodate for w.height
+ if self.height < row + w.height:
+ for _i in range(row + w.height - self.height):
+ self._buffer.append(list())
+
+ # append columns to accommodate for w.width
+ for l in range(row, row + w.height):
+ l_len = len(self._buffer[l])
+ w_len = len(w.content[l - row])
+ if l_len < col + w_len:
+ self._buffer[l] += ((col + w_len - l_len) * list(u" "))
+ self._buffer[l][col:col + w_len] = w.content[l - row][:]
+
+ # move the cursor to new spot
+ if block:
+ self._cursor = (row + w.height, col)
+ else:
+ self._cursor = (row + w.height, 0)
+
+ def write(self, text, row=None, col=None, width=None, block=False, wordwrap=False):
+ """Emulate the typing machine writing to this widget's buffer.
+
+ :param text: text to type
+ :type text: str
+
+ :param row: row number to start at (default is at the cursor position)
+ :type row: int
+
+ :param col: column number to start at (default is at the cursor position)
+ :type col: int
+
+ :param width: wrap at "col" + "width" column (default is at self._max_width)
+ :type width: int
+
+ :param block: when printing newline, start at column col (True) or at column 0 (False)
+ :type block: boolean
+
+ :param wordwrap: wrap by words
+ :type wordwrap: boolean
+ """
+ if not text:
+ return
+
+ text = ensure_str(text)
+ if row is None:
+ row = self._cursor[0]
+
+ if col is None:
+ col = self._cursor[1]
+
+ if width is None and self._max_width:
+ width = self._max_width - col
+
+ x = row
+ y = col
+
+ if wordwrap:
+ text = self._wrap_words(text, width)
+
+ # emulate typing machine
+ for character in text:
+ # FIXME: Remove the code duplication below and optimize it
+ # process newline
+ if character == "\n":
+ x += 1
+ if block:
+ y = col
+ else:
+ y = 0
+
+ self._increase_x_buffer_size(x)
+ continue
+
+ self._increase_x_buffer_size(x)
+ self._increase_y_buffer_size(x, y)
+
+ self._save_character_to_buffer(x, y, character)
+
+ # shift to the next char
+ y += 1
+ if width is not None and y >= col + width:
+ x += 1
+ if block:
+ y = col
+ else:
+ y = 0
+
+ self._cursor = (x, y)
+
+ def _increase_x_buffer_size(self, x):
+ if x >= len(self._buffer):
+ for _i in range(x - len(self._buffer) + 1):
+ self._buffer.append(list())
+
+ def _increase_y_buffer_size(self, x, y):
+ if y >= len(self._buffer[x]):
+ self._buffer[x] += ((y - len(self._buffer[x]) + 1) * list(u" "))
+
+ def _save_character_to_buffer(self, x, y, character):
+ self._buffer[x][y] = character
+
+ @staticmethod
+ def _wrap_words(text, width):
+ lines = []
+ # Wrap each line separately
+ for line in text.split('\n'):
+ sublines = []
+ for subline in wrap(line, width):
+ sublines.append(subline)
+ if len(subline) < width:
+ # line shorter than width will be wrapped by '\n' we add
+ sublines.append('\n')
+ # line with length == width will be wrapped by the width based
+ # wrapping logic
+ # end of line will be wrapped by '\n' following the line in
+ # original text
+ if sublines and sublines[-1] == '\n':
+ sublines.pop()
+ lines.append("".join(sublines))
+ return '\n'.join(lines)
+
+
+class TextWidget(Widget):
+ """Class to handle wrapped text output."""
+
+ def __init__(self, text):
+ """
+ :param text: text to format
+ :type text: str
+ """
+ super().__init__()
+ self._text = text
+
+ @property
+ def text(self):
+ """Contains text of this widget."""
+ return self._text
+
+ def render(self, width):
+ """Renders the text widget limited to width number of columns.
+
+ Wraps to the next line when the text is longer.
+
+ :param width: maximum width allocated to the string
+ :type width: int
+ """
+ super().render(width)
+ self.write(self._text, width=width, wordwrap=True)
+
+
+class EntryWidget(TextWidget):
+ """This is the easy way how to generate entry items for containers.
+
+ If the numbering in a container is turned on the output looks like:
+
+ N) title
+ value
+
+ Without numbering turned on:
+
+ title
+ value
+ """
+
+ def __init__(self, title, value=None):
+ """ Create Entry widget instance.
+
+ :param title: Title of this entry.
+ :type title: String.
+
+ :param value: Actual value printed in second line below the title.
+ :type value: String.
+ """
+ text = self._create_text(title=title, value=value)
+ super().__init__(text)
+
+ @staticmethod
+ def _create_text(title, value):
+ msg = title
+
+ if value:
+ msg += "\n"
+ msg += value
+
+ return msg
+
+
+class SeparatorWidget(Widget):
+ """Print empty line."""
+
+ def __init__(self, lines=1):
+ """Construct SeparatorWidget for printing blank lines.
+
+ :param lines: How many lines should be blank.
+ :type lines: int greater than 0.
+ """
+ super().__init__()
+ self._lines = lines
+
+ def render(self, width):
+ """Render empty line to the buffer.
+
+ :param width: maximum width allocated to the string
+ :type width: int
+ """
+ super().render(width)
+ self.write("")
+
+ def write(self, text, row=None, col=None, width=None, block=False, wordwrap=False):
+ """Optimize write function.
+
+ To print just a blank line we don't need too much logic.
+ """
+ for i in range(0, self._lines):
+ self._buffer.append(list())
+ self._buffer[i] += u""
+ self.set_cursor_position(self._lines - 1, 0)
+
+
+class CenterWidget(Widget):
+ """Class to handle horizontal centering of content."""
+
+ def __init__(self, w):
+ """
+ :param w: widget to center
+ :type w: Widget
+ """
+ super().__init__()
+ self._w = w
+
+ def render(self, width):
+ """Render the centered widget to internal buffer.
+
+ :param width: maximum width the widget should use
+ :type width: int
+ """
+ super().render(width)
+ self._w.render(width)
+ # make sure col is an integer
+ self.draw(self._w, col=(width - self._w.width) // 2)
+
+
+class CheckboxWidget(Widget):
+ """Widget to show checkbox with (un)checked box, name and description."""
+
+ def __init__(self, key="x", title=None, text=None, completed=None):
+ """
+ :param key: tick character to be used inside [ ]
+ :type key: character
+
+ :param title: the title next to the [ ] box
+ :type title: str
+
+ :param text: the description text to be shown on the second row in ()
+ :type text: str
+
+ :param completed: is the checkbox ticked or not?
+ :type completed: True|False
+ """
+ super().__init__()
+ self._key = key
+ self._title = title
+ self._text = text
+ self._completed = completed
+
+ def render(self, width):
+ """Render the widget to internal buffer.
+
+ It should be max width characters wide.
+ """
+ super().render(width)
+
+ if self.completed:
+ checkchar = self._key
+ else:
+ checkchar = " "
+
+ # prepare the checkbox
+ checkbox = TextWidget("[%s]" % checkchar)
+
+ data = []
+
+ # append lines
+ if self.title:
+ data.append(TextWidget(_(self.title)))
+
+ if self.text:
+ data.append(TextWidget("(%s)" % self.text))
+
+ # the checkbox has two columns
+ # [x] is one and is 3 chars wide
+ # text is second and can occupy width - 3 - 1 (for space) chars
+ cols = ColumnWidget([(3, [checkbox]), (width - 4, data)], 1)
+ cols.render(width)
+
+ # transfer the column widget rendered stuff to internal buffer
+ self.draw(cols)
+
+ @property
+ def title(self):
+ """Returns the first line (main title) of the checkbox."""
+ return self._title
+
+ @property
+ def completed(self):
+ """Returns the state of the checkbox, checked is True."""
+ return self._completed
+
+ @property
+ def text(self):
+ """Contains the description text from the second line."""
+ return self._text
+
+
+class ColumnWidget(Widget):
+
+ def __init__(self, columns, spacing=0):
+ """Create text columns
+
+ Deprecated. Please do not use this widget, use containers instead.
+
+ :param columns: list containing (column width, [list of widgets to put into this column])
+ :type columns: [(int, [...]), ...]
+
+ :param spacing: number of spaces to use between columns
+ :type spacing: int
+ """
+ super().__init__()
+ self._spacing = spacing
+ self._columns = columns
+
+ def render(self, width):
+ """Render the widget to it's internal buffer
+
+ :param width: the maximum width the widget can use
+ :type width: int
+
+ :return: nothing
+ """
+ super().render(width)
+
+ # the leftmost empty column
+ col_pos = 0
+
+ # iterate over tuples (column width, column content)
+ for col_width, col in self._columns:
+
+ # set cursor to first line and leftmost empty column
+ self.set_cursor_position(0, col_pos)
+
+ # if requested width is None, limit the maximum to width
+ # and set minimum to 0
+ if col_width is None:
+ col_max_width = width - self.cursor[1]
+ col_width = 0
+ else:
+ col_max_width = col_width
+
+ # render and draw contents of column
+ for item in col:
+ item.render(col_max_width)
+ self.draw(item, block=True)
+
+ # recompute the leftmost empty column
+ col_pos = max((col_pos + col_width), self.width) + self._spacing
diff --git a/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/utils/__init__.py b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/utils/__init__.py
new file mode 100644
index 0000000..bb1622c
--- /dev/null
+++ b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/utils/__init__.py
@@ -0,0 +1,94 @@
+# iutil.py - generic install utility functions
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+
+import sys
+import string # pylint: disable=deprecated-module
+import unicodedata
+
+
+def ensure_str(str_or_bytes, keep_none=True):
+ """
+ Returns a str instance for given string or ``None`` if requested to keep it.
+
+ :param str_or_bytes: string to be kept or converted to str type
+ :type str_or_bytes: str or bytes
+ :param bool keep_none: whether to keep None as it is or raise ValueError if
+ ``None`` is passed
+ :raises ValueError: if applied on an object not being of type bytes nor str
+ (nor NoneType if ``keep_none`` is ``False``)
+ """
+ if keep_none and str_or_bytes is None:
+ return None
+ if isinstance(str_or_bytes, str):
+ return str_or_bytes
+ if isinstance(str_or_bytes, bytes):
+ return str_or_bytes.decode(sys.getdefaultencoding())
+
+ raise ValueError(
+ "str_or_bytes must be of type 'str' or 'bytes', not '%s'" % type(str_or_bytes))
+
+
+# Define translations between ASCII uppercase and lowercase for
+# locale-independent string conversions. The tables are 256-byte string used
+# with str.translate. If str.translate is used with a unicode string,
+# even if the string contains only 7-bit characters, str.translate will
+# raise a UnicodeDecodeError.
+_ASCIIlower_table = str.maketrans(string.ascii_uppercase, string.ascii_lowercase)
+_ASCIIupper_table = str.maketrans(string.ascii_lowercase, string.ascii_uppercase)
+
+
+def _toASCII(s):
+ """Convert a unicode string to ASCII"""
+ if isinstance(s, str):
+ # Decompose the string using the NFK decomposition, which in addition
+ # to the canonical decomposition replaces characters based on
+ # compatibility equivalence (e.g., ROMAN NUMERAL ONE has its own code
+ # point but it's really just a capital I), so that we can keep as much
+ # of the ASCII part of the string as possible.
+ s = unicodedata.normalize('NFKD', s).encode('ascii', 'ignore').decode("ascii")
+ elif not isinstance(s, bytes):
+ s = ''
+ return s
+
+
+def lowerASCII(s):
+ """Convert a string to lowercase using only ASCII character definitions.
+
+ The returned string will contain only ASCII characters. This function is
+ locale-independent.
+ """
+ # XXX: Python 3 has str.maketrans() and bytes.maketrans() so we should
+ # ideally use one or the other depending on the type of 's'. But it turns
+ # out we expect this function to always return string even if given bytes.
+ s = ensure_str(s)
+ return str.translate(_toASCII(s), _ASCIIlower_table)
+
+
+def upperASCII(s):
+ """Convert a string to uppercase using only ASCII character definitions.
+
+ The returned string will contain only ASCII characters. This function is
+ locale-independent.
+ """
+ # XXX: Python 3 has str.maketrans() and bytes.maketrans() so we should
+ # ideally use one or the other depending on the type of 's'. But it turns
+ # out we expect this function to always return string even if given bytes.
+ s = ensure_str(s)
+ return str.translate(_toASCII(s), _ASCIIupper_table)
diff --git a/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/utils/__pycache__/__init__.cpython-314.pyc b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/utils/__pycache__/__init__.cpython-314.pyc
new file mode 100644
index 0000000..4c95913
Binary files /dev/null and b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/utils/__pycache__/__init__.cpython-314.pyc differ
diff --git a/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/utils/__pycache__/i18n.cpython-314.pyc b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/utils/__pycache__/i18n.cpython-314.pyc
new file mode 100644
index 0000000..368f88d
Binary files /dev/null and b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/utils/__pycache__/i18n.cpython-314.pyc differ
diff --git a/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/utils/i18n.py b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/utils/i18n.py
new file mode 100644
index 0000000..c740c34
--- /dev/null
+++ b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/lib/python3.14/site-packages/simpleline/utils/i18n.py
@@ -0,0 +1,61 @@
+# Translation functions we use all over the place
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+
+__all__ = ["_", "N_", "P_", "C_", "CN_", "CP_"]
+
+import gettext
+
+N_ = lambda x: x
+_ = lambda x: gettext.translation("python-simpleline", fallback=True).gettext(x) if x != "" else ""
+P_ = lambda x, y, z: gettext.translation("python-simpleline", fallback=True).ngettext(x, y, z)
+
+# This is equivalent to "pgettext" in GNU gettext. The pgettext functions
+# are not exported by Python, but all they really do is a stick a EOT
+# character between msgctxt and msgid and check that msgctxt isn't part
+# of the return value.
+
+
+def C_(msgctxt, msgid):
+ ctxid = "%s\x04%s" % (msgctxt, msgid)
+ translation = _(ctxid)
+
+ # If there is no translation for msgctxtmsgid, return only msgid
+ if translation == ctxid:
+ return msgid
+
+ return translation
+
+# Mark as translatable with context
+CN_ = lambda c, x: x
+
+# npgettext; i.e., gettext with plural form and context
+
+
+def CP_(msgctxt, msgid, msgid_plural, n):
+ ctxid = "%s\x04%s" % (msgctxt, msgid)
+ translation = P_(ctxid, msgid_plural, n)
+
+ # If the returned value is msgctxtmsgid, ngettext was trying to
+ # fallback to msgid. We don't add msgctxt to msgid_plural, so any other
+ # return value is correct.
+ if translation == ctxid:
+ return msgid
+
+ return translation
diff --git a/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/share/licenses/python-simpleline/LICENSE.md b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/share/licenses/python-simpleline/LICENSE.md
new file mode 100644
index 0000000..0927556
--- /dev/null
+++ b/pkgbuilds/python-simpleline/pkg/python-simpleline/usr/share/licenses/python-simpleline/LICENSE.md
@@ -0,0 +1,157 @@
+### GNU LESSER GENERAL PUBLIC LICENSE
+
+Version 3, 29 June 2007
+
+Copyright (C) 2007 Free Software Foundation, Inc.
+
+
+Everyone is permitted to copy and distribute verbatim copies of this
+license document, but changing it is not allowed.
+
+This version of the GNU Lesser General Public License incorporates the
+terms and conditions of version 3 of the GNU General Public License,
+supplemented by the additional permissions listed below.
+
+#### 0. Additional Definitions.
+
+As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the
+GNU General Public License.
+
+"The Library" refers to a covered work governed by this License, other
+than an Application or a Combined Work as defined below.
+
+An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+#### 1. Exception to Section 3 of the GNU GPL.
+
+You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+#### 2. Conveying Modified Versions.
+
+If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+- a) under this License, provided that you make a good faith effort
+ to ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+- b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+#### 3. Object Code Incorporating Material from Library Header Files.
+
+The object code form of an Application may incorporate material from a
+header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+- a) Give prominent notice with each copy of the object code that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+- b) Accompany the object code with a copy of the GNU GPL and this
+ license document.
+
+#### 4. Combined Works.
+
+You may convey a Combined Work under terms of your choice that, taken
+together, effectively do not restrict modification of the portions of
+the Library contained in the Combined Work and reverse engineering for
+debugging such modifications, if you also do each of the following:
+
+- a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+- b) Accompany the Combined Work with a copy of the GNU GPL and this
+ license document.
+- c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+- d) Do one of the following:
+ - 0) Convey the Minimal Corresponding Source under the terms of
+ this License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+ - 1) Use a suitable shared library mechanism for linking with
+ the Library. A suitable mechanism is one that (a) uses at run
+ time a copy of the Library already present on the user's
+ computer system, and (b) will operate properly with a modified
+ version of the Library that is interface-compatible with the
+ Linked Version.
+- e) Provide Installation Information, but only if you would
+ otherwise be required to provide such information under section 6
+ of the GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the Application
+ with a modified version of the Linked Version. (If you use option
+ 4d0, the Installation Information must accompany the Minimal
+ Corresponding Source and Corresponding Application Code. If you
+ use option 4d1, you must provide the Installation Information in
+ the manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.)
+
+#### 5. Combined Libraries.
+
+You may place library facilities that are a work based on the Library
+side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+- a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities, conveyed under the terms of this License.
+- b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+#### 6. Revised Versions of the GNU Lesser General Public License.
+
+The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+as you received it specifies that a certain numbered version of the
+GNU Lesser General Public License "or any later version" applies to
+it, you have the option of following the terms and conditions either
+of that published version or of any later version published by the
+Free Software Foundation. If the Library as you received it does not
+specify a version number of the GNU Lesser General Public License, you
+may choose any version of the GNU Lesser General Public License ever
+published by the Free Software Foundation.
+
+If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
diff --git a/pkgbuilds/python-simpleline/python-simpleline-1.8-1-any.pkg.tar.zst b/pkgbuilds/python-simpleline/python-simpleline-1.8-1-any.pkg.tar.zst
new file mode 100644
index 0000000..5b08233
Binary files /dev/null and b/pkgbuilds/python-simpleline/python-simpleline-1.8-1-any.pkg.tar.zst differ
diff --git a/pkgbuilds/python-simpleline/python-simpleline-1.8.tar.gz b/pkgbuilds/python-simpleline/python-simpleline-1.8.tar.gz
new file mode 100644
index 0000000..ba895b4
Binary files /dev/null and b/pkgbuilds/python-simpleline/python-simpleline-1.8.tar.gz differ
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-1.8.tar.gz b/pkgbuilds/python-simpleline/src/python-simpleline-1.8.tar.gz
new file mode 120000
index 0000000..082f130
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-1.8.tar.gz
@@ -0,0 +1 @@
+/home/nocode/Files/FWS-ISO/pkgbuilds/python-simpleline/python-simpleline-1.8.tar.gz
\ No newline at end of file
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/.github/workflows/validate.yml b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/.github/workflows/validate.yml
new file mode 100644
index 0000000..8de6b5b
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/.github/workflows/validate.yml
@@ -0,0 +1,16 @@
+name: validate
+on: [push, pull_request]
+jobs:
+ rawhide:
+ runs-on: ubuntu-latest
+ container:
+ image: registry.fedoraproject.org/fedora:rawhide
+ steps:
+ - name: Clone repository
+ uses: actions/checkout@v2
+
+ - name: install test dependencies
+ run: dnf install -y python3-pylint python3-gobject-base make
+
+ - name: run tests
+ run: make ci
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/.gitignore b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/.gitignore
new file mode 100644
index 0000000..d607e1b
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/.gitignore
@@ -0,0 +1,23 @@
+*.pyc
+.*.swp
+
+.coverage
+tests/coverage-report.log
+
+ChangeLog
+
+po/*.gmo
+po/*.po
+po/python-simpleline.pot
+po/POTFILES
+
+tests/pylint/.pylint.d/
+pylint-log
+
+build/
+dist/
+simpleline.egg-info/
+
+.directory
+
+doc/_build
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/.packit.yaml b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/.packit.yaml
new file mode 100644
index 0000000..a9974f7
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/.packit.yaml
@@ -0,0 +1,46 @@
+specfile_path: python-simpleline.spec
+upstream_package_name: simpleline
+upstream_tag_template: simpleline-{version}
+actions:
+ create-archive:
+ - "make BUILD_ARGS=sdist archive"
+ - 'bash -c "cp dist/*.tar.gz ."'
+ - 'bash -c "ls *.tar.gz"'
+jobs:
+ - job: propose_downstream
+ trigger: release
+ metadata:
+ dist_git_branches: fedora-development
+
+ - job: tests
+ trigger: pull_request
+ metadata:
+ targets:
+ - fedora-all
+
+ - job: copr_build
+ trigger: pull_request
+ metadata:
+ targets:
+ - fedora-eln
+
+ - job: copr_build
+ trigger: commit
+ metadata:
+ targets:
+ - fedora-rawhide
+ - fedora-eln
+ branch: master
+ owner: "@rhinstaller"
+ project: Anaconda
+ preserve_project: True
+
+ - job: copr_build
+ trigger: commit
+ metadata:
+ targets:
+ - fedora-34
+ branch: master
+ owner: "@rhinstaller"
+ project: Anaconda-devel
+ preserve_project: True
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/.pylintrc b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/.pylintrc
new file mode 100644
index 0000000..a9d4ab1
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/.pylintrc
@@ -0,0 +1,504 @@
+[MASTER]
+
+# A comma-separated list of package or module names from where C extensions may
+# be loaded. Extensions are loading into the active Python interpreter and may
+# run arbitrary code.
+extension-pkg-whitelist=
+
+# Add files or directories to the blacklist. They should be base names, not
+# paths.
+ignore=
+
+# Add files or directories matching the regex patterns to the blacklist. The
+# regex matches against base names, not paths.
+ignore-patterns=
+
+# Python code to execute, usually for sys.path manipulation such as
+# pygtk.require().
+#init-hook=
+
+# Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the
+# number of processors available to use.
+jobs=0
+
+# Control the amount of potential inferred values when inferring a single
+# object. This can help the performance when dealing with large functions or
+# complex, nested conditions.
+limit-inference-results=100
+
+# List of plugins (as comma separated values of python modules names) to load,
+# usually to register additional checkers.
+load-plugins=
+
+# Pickle collected data for later comparisons.
+persistent=yes
+
+# Specify a configuration file.
+#rcfile=
+
+# When enabled, pylint would attempt to guess common misconfiguration and emit
+# user-friendly hints instead of false-positive error messages.
+suggestion-mode=yes
+
+# Allow loading of arbitrary C extensions. Extensions are imported into the
+# active Python interpreter and may run arbitrary code.
+unsafe-load-any-extension=no
+
+
+[MESSAGES CONTROL]
+
+# Only show warnings with the listed confidence levels. Leave empty to show
+# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED.
+confidence=
+
+# Disable the message, report, category or checker with the given id(s). You
+# can either give multiple identifiers separated by comma (,) or put this
+# option multiple times (only on the command line, not in the configuration
+# file where it should appear only once). You can also use "--disable=all" to
+# disable everything first and then reenable specific checks. For example, if
+# you want to run only the similarities checker, you can use "--disable=all
+# --enable=similarities". If you want to run only the classes checker, but have
+# no Warning level messages displayed, use "--disable=all --enable=classes
+# --disable=W".
+disable=fixme,
+ invalid-name,
+ missing-docstring,
+ no-self-use,
+ too-few-public-methods,
+ too-many-arguments,
+ too-many-instance-attributes,
+ too-many-public-methods,
+ too-many-return-statements,
+ unnecessary-pass,
+ unused-argument,
+ useless-return,
+
+# Enable the message, report, category or checker with the given id(s). You can
+# either give multiple identifier separated by comma (,) or put this option
+# multiple time (only on the command line, not in the configuration file where
+# it should appear only once). See also the "--disable" option for examples.
+#enable=c-extension-no-member
+
+
+[REPORTS]
+
+# Python expression which should return a note less than 10 (10 is the highest
+# note). You have access to the variables errors warning, statement which
+# respectively contain the number of errors / warnings messages and the total
+# number of statements analyzed. This is used by the global evaluation report
+# (RP0004).
+evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
+
+# Template used to display messages. This is a python new-style format string
+# used to format the message information. See doc for all details.
+msg-template='{msg_id}({symbol}):{path}:{line},{column}: {obj}: {msg}'
+
+# Set the output format. Available formats are text, parseable, colorized, json
+# and msvs (visual studio). You can also give a reporter class, e.g.
+# mypackage.mymodule.MyReporterClass.
+output-format=colorized
+
+# Tells whether to display a full report or only the messages.
+reports=no
+
+# Activate the evaluation score.
+score=no
+
+
+[REFACTORING]
+
+# Maximum number of nested blocks for function / method body
+max-nested-blocks=5
+
+# Complete name of functions that never returns. When checking for
+# inconsistent-return-statements if a never returning function is called then
+# it will be considered as an explicit return statement and no message will be
+# printed.
+never-returning-functions=sys.exit
+
+
+[BASIC]
+
+# Naming style matching correct argument names.
+argument-naming-style=snake_case
+
+# Regular expression matching correct argument names. Overrides argument-
+# naming-style.
+#argument-rgx=
+
+# Naming style matching correct attribute names.
+attr-naming-style=snake_case
+
+# Regular expression matching correct attribute names. Overrides attr-naming-
+# style.
+#attr-rgx=
+
+# Bad variable names which should always be refused, separated by a comma.
+bad-names=foo,
+ bar,
+ baz,
+ toto,
+ tutu,
+ tata
+
+# Naming style matching correct class attribute names.
+class-attribute-naming-style=any
+
+# Regular expression matching correct class attribute names. Overrides class-
+# attribute-naming-style.
+#class-attribute-rgx=
+
+# Naming style matching correct class names.
+class-naming-style=PascalCase
+
+# Regular expression matching correct class names. Overrides class-naming-
+# style.
+#class-rgx=
+
+# Naming style matching correct constant names.
+const-naming-style=UPPER_CASE
+
+# Regular expression matching correct constant names. Overrides const-naming-
+# style.
+#const-rgx=
+
+# Minimum line length for functions/classes that require docstrings, shorter
+# ones are exempt.
+docstring-min-length=-1
+
+# Naming style matching correct function names.
+function-naming-style=snake_case
+
+# Regular expression matching correct function names. Overrides function-
+# naming-style.
+#function-rgx=
+
+# Good variable names which should always be accepted, separated by a comma.
+good-names=i,
+ j,
+ k,
+ ex,
+ Run,
+ _
+
+# Include a hint for the correct naming format with invalid-name.
+include-naming-hint=no
+
+# Naming style matching correct inline iteration names.
+inlinevar-naming-style=any
+
+# Regular expression matching correct inline iteration names. Overrides
+# inlinevar-naming-style.
+#inlinevar-rgx=
+
+# Naming style matching correct method names.
+method-naming-style=snake_case
+
+# Regular expression matching correct method names. Overrides method-naming-
+# style.
+#method-rgx=
+
+# Naming style matching correct module names.
+module-naming-style=snake_case
+
+# Regular expression matching correct module names. Overrides module-naming-
+# style.
+#module-rgx=
+
+# Colon-delimited sets of names that determine each other's naming style when
+# the name regexes allow several styles.
+name-group=
+
+# Regular expression which should only match function or class names that do
+# not require a docstring.
+no-docstring-rgx=^_
+
+# List of decorators that produce properties, such as abc.abstractproperty. Add
+# to this list to register other decorators that produce valid properties.
+# These decorators are taken in consideration only for invalid-name.
+property-classes=abc.abstractproperty
+
+# Naming style matching correct variable names.
+variable-naming-style=snake_case
+
+# Regular expression matching correct variable names. Overrides variable-
+# naming-style.
+#variable-rgx=
+
+
+[FORMAT]
+
+# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.
+expected-line-ending-format=
+
+# Regexp for a line that is allowed to be longer than the limit.
+ignore-long-lines=^\s*(# )??$
+
+# Number of spaces of indent required inside a hanging or continued line.
+indent-after-paren=4
+
+# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
+# tab).
+indent-string=' '
+
+# Maximum number of characters on a single line.
+max-line-length=99
+
+# Maximum number of lines in a module.
+max-module-lines=1000
+
+# List of optional constructs for which whitespace checking is disabled. `dict-
+# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}.
+# `trailing-comma` allows a space between comma and closing bracket: (a, ).
+# `empty-line` allows space-only lines.
+no-space-check=trailing-comma,
+ dict-separator
+
+# Allow the body of a class to be on the same line as the declaration if body
+# contains single statement.
+single-line-class-stmt=no
+
+# Allow the body of an if to be on the same line as the test if there is no
+# else.
+single-line-if-stmt=no
+
+
+[LOGGING]
+
+# Format style used to check logging format string. `old` means using %
+# formatting, while `new` is for `{}` formatting.
+logging-format-style=old
+
+# Logging modules to check that the string format arguments are in logging
+# function parameter format.
+logging-modules=logging
+
+
+[MISCELLANEOUS]
+
+# List of note tags to take in consideration, separated by a comma.
+notes=FIXME,
+ XXX,
+ TODO
+
+
+[SIMILARITIES]
+
+# Ignore comments when computing similarities.
+ignore-comments=yes
+
+# Ignore docstrings when computing similarities.
+ignore-docstrings=yes
+
+# Ignore imports when computing similarities.
+ignore-imports=no
+
+# Minimum lines number of a similarity.
+min-similarity-lines=4
+
+
+[SPELLING]
+
+# Limits count of emitted suggestions for spelling mistakes.
+max-spelling-suggestions=4
+
+# Spelling dictionary name. Available dictionaries: en_US (myspell), cs_CZ
+# (myspell), en_GB (myspell), en_AG (myspell), en_AU (myspell), en_BS
+# (myspell), en_BW (myspell), en_BZ (myspell), en_CA (myspell), en_DK
+# (myspell), en_GH (myspell), en_HK (myspell), en_IE (myspell), en_IN
+# (myspell), en_JM (myspell), en_MW (myspell), en_NA (myspell), en_NG
+# (myspell), en_NZ (myspell), en_PH (myspell), en_SG (myspell), en_TT
+# (myspell), en_ZA (myspell), en_ZM (myspell), en_ZW (myspell)..
+spelling-dict=
+
+# List of comma separated words that should not be checked.
+spelling-ignore-words=
+
+# A path to a file that contains private dictionary; one word per line.
+spelling-private-dict-file=
+
+# Tells whether to store unknown words to indicated private dictionary in
+# --spelling-private-dict-file option instead of raising a message.
+spelling-store-unknown-words=no
+
+
+[STRING]
+
+# This flag controls whether the implicit-str-concat-in-sequence should
+# generate a warning on implicit string concatenation in sequences defined over
+# several lines.
+check-str-concat-over-line-jumps=no
+
+
+[TYPECHECK]
+
+# List of decorators that produce context managers, such as
+# contextlib.contextmanager. Add to this list to register other decorators that
+# produce valid context managers.
+contextmanager-decorators=contextlib.contextmanager
+
+# List of members which are set dynamically and missed by pylint inference
+# system, and so shouldn't trigger E1101 when accessed. Python regular
+# expressions are accepted.
+generated-members=
+
+# Tells whether missing members accessed in mixin class should be ignored. A
+# mixin class is detected if its name ends with "mixin" (case insensitive).
+ignore-mixin-members=yes
+
+# Tells whether to warn about missing members when the owner of the attribute
+# is inferred to be None.
+ignore-none=yes
+
+# This flag controls whether pylint should warn about no-member and similar
+# checks whenever an opaque object is returned when inferring. The inference
+# can return multiple potential results while evaluating a Python object, but
+# some branches might not be evaluated, which results in partial inference. In
+# that case, it might be useful to still emit no-member and other checks for
+# the rest of the inferred objects.
+ignore-on-opaque-inference=yes
+
+# List of class names for which member attributes should not be checked (useful
+# for classes with dynamically set attributes). This supports the use of
+# qualified names.
+ignored-classes=DefaultInstall,Popen,QueueFactory,TransactionSet,Namespace
+
+# List of module names for which member attributes should not be checked
+# (useful for modules/projects where namespaces are manipulated during runtime
+# and thus existing member attributes cannot be deduced by static analysis. It
+# supports qualified module names, as well as Unix pattern matching.
+ignored-modules=
+
+# Show a hint with possible names when a member name was not found. The aspect
+# of finding the hint is based on edit distance.
+missing-member-hint=yes
+
+# The minimum edit distance a name should have in order to be considered a
+# similar match for a missing member name.
+missing-member-hint-distance=1
+
+# The total number of similar names that should be taken in consideration when
+# showing a hint for a missing member.
+missing-member-max-choices=1
+
+
+[VARIABLES]
+
+# List of additional names supposed to be defined in builtins. Remember that
+# you should avoid defining new builtins when possible.
+additional-builtins=
+
+# Tells whether unused global variables should be treated as a violation.
+allow-global-unused-variables=yes
+
+# List of strings which can identify a callback function by name. A callback
+# name must start or end with one of those strings.
+# callbacks=cb_,
+# _cb
+
+# A regular expression matching the name of dummy variables (i.e. expected to
+# not be used).
+dummy-variables-rgx=_
+
+# Argument names that match this expression will be ignored. Default to name
+# with leading underscore.
+ignored-argument-names=_.*|^ignored_|^unused_
+
+# Tells whether we should check for unused import in __init__ files.
+init-import=yes
+
+# List of qualified module names which can have objects that can redefine
+# builtins.
+redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io
+
+
+[CLASSES]
+
+# List of method names used to declare (i.e. assign) instance attributes.
+defining-attr-methods=__init__,
+ __new__,
+ setUp
+
+# List of member names, which should be excluded from the protected access
+# warning.
+exclude-protected=
+
+# List of valid names for the first argument in a class method.
+valid-classmethod-first-arg=cls
+
+# List of valid names for the first argument in a metaclass class method.
+valid-metaclass-classmethod-first-arg=cls
+
+
+[DESIGN]
+
+# Maximum number of arguments for function / method.
+max-args=5
+
+# Maximum number of attributes for a class (see R0902).
+max-attributes=7
+
+# Maximum number of boolean expressions in an if statement.
+max-bool-expr=5
+
+# Maximum number of branch for function / method body.
+max-branches=12
+
+# Maximum number of locals for function / method body.
+max-locals=15
+
+# Maximum number of parents for a class (see R0901).
+max-parents=7
+
+# Maximum number of public methods for a class (see R0904).
+max-public-methods=20
+
+# Maximum number of return / yield for function / method body.
+max-returns=6
+
+# Maximum number of statements in function / method body.
+max-statements=50
+
+# Minimum number of public methods for a class (see R0903).
+min-public-methods=2
+
+
+[IMPORTS]
+
+# Allow wildcard imports from modules that define __all__.
+allow-wildcard-with-all=no
+
+# Analyse import fallback blocks. This can be used to support both Python 2 and
+# 3 compatible code, which means that the block might have code that exists
+# only in one or another interpreter, leading to false positives when analysed.
+analyse-fallback-blocks=no
+
+# Deprecated modules which should not be used, separated by a comma.
+deprecated-modules=optparse,tkinter.tix,string,regsub,TERMINOS,Bastion,rexec,mock
+
+# Create a graph of external dependencies in the given file (report RP0402 must
+# not be disabled).
+ext-import-graph=
+
+# Create a graph of every (i.e. internal and external) dependencies in the
+# given file (report RP0402 must not be disabled).
+import-graph=
+
+# Create a graph of internal dependencies in the given file (report RP0402 must
+# not be disabled).
+int-import-graph=
+
+# Force import order to recognize a module as part of the standard
+# compatibility libraries.
+known-standard-library=
+
+# Force import order to recognize a module as part of a third party library.
+known-third-party=enchant
+
+
+[EXCEPTIONS]
+
+# Exceptions that will emit a warning when being caught. Defaults to
+# "BaseException, Exception".
+overgeneral-exceptions=BaseException,
+ Exception
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/LICENSE.md b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/LICENSE.md
new file mode 100644
index 0000000..0927556
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/LICENSE.md
@@ -0,0 +1,157 @@
+### GNU LESSER GENERAL PUBLIC LICENSE
+
+Version 3, 29 June 2007
+
+Copyright (C) 2007 Free Software Foundation, Inc.
+
+
+Everyone is permitted to copy and distribute verbatim copies of this
+license document, but changing it is not allowed.
+
+This version of the GNU Lesser General Public License incorporates the
+terms and conditions of version 3 of the GNU General Public License,
+supplemented by the additional permissions listed below.
+
+#### 0. Additional Definitions.
+
+As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the
+GNU General Public License.
+
+"The Library" refers to a covered work governed by this License, other
+than an Application or a Combined Work as defined below.
+
+An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+#### 1. Exception to Section 3 of the GNU GPL.
+
+You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+#### 2. Conveying Modified Versions.
+
+If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+- a) under this License, provided that you make a good faith effort
+ to ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+- b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+#### 3. Object Code Incorporating Material from Library Header Files.
+
+The object code form of an Application may incorporate material from a
+header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+- a) Give prominent notice with each copy of the object code that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+- b) Accompany the object code with a copy of the GNU GPL and this
+ license document.
+
+#### 4. Combined Works.
+
+You may convey a Combined Work under terms of your choice that, taken
+together, effectively do not restrict modification of the portions of
+the Library contained in the Combined Work and reverse engineering for
+debugging such modifications, if you also do each of the following:
+
+- a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+- b) Accompany the Combined Work with a copy of the GNU GPL and this
+ license document.
+- c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+- d) Do one of the following:
+ - 0) Convey the Minimal Corresponding Source under the terms of
+ this License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+ - 1) Use a suitable shared library mechanism for linking with
+ the Library. A suitable mechanism is one that (a) uses at run
+ time a copy of the Library already present on the user's
+ computer system, and (b) will operate properly with a modified
+ version of the Library that is interface-compatible with the
+ Linked Version.
+- e) Provide Installation Information, but only if you would
+ otherwise be required to provide such information under section 6
+ of the GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the Application
+ with a modified version of the Linked Version. (If you use option
+ 4d0, the Installation Information must accompany the Minimal
+ Corresponding Source and Corresponding Application Code. If you
+ use option 4d1, you must provide the Installation Information in
+ the manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.)
+
+#### 5. Combined Libraries.
+
+You may place library facilities that are a work based on the Library
+side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+- a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities, conveyed under the terms of this License.
+- b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+#### 6. Revised Versions of the GNU Lesser General Public License.
+
+The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+as you received it specifies that a certain numbered version of the
+GNU Lesser General Public License "or any later version" applies to
+it, you have the option of following the terms and conditions either
+of that published version or of any later version published by the
+Free Software Foundation. If the Library as you received it does not
+specify a version number of the GNU Lesser General Public License, you
+may choose any version of the GNU Lesser General Public License ever
+published by the Free Software Foundation.
+
+If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/MANIFEST.in b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/MANIFEST.in
new file mode 100644
index 0000000..bd2f3e5
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/MANIFEST.in
@@ -0,0 +1,4 @@
+include LICENSE.md Makefile ChangeLog README.md
+recursive-include simpleline *.py
+recursive-include po *.po *.pot Makefile
+recursive-include tests *.py *.sh
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/Makefile b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/Makefile
new file mode 100644
index 0000000..476b951
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/Makefile
@@ -0,0 +1,154 @@
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+
+PKGNAME=simpleline
+SPECNAME=python-$(PKGNAME)
+VERSION=$(shell awk '/Version:/ { print $$2 }' $(SPECNAME).spec)
+RELEASE=$(shell awk '/Release:/ { print $$2 }' $(SPECNAME).spec | sed -e 's|%.*$$||g')
+TAG=$(PKGNAME)-$(VERSION)
+
+PYTHON?=python3
+COVERAGE?=coverage3
+
+# Arguments used for setup.py call for creating archive
+BUILD_ARGS ?= sdist bdist_wheel
+
+# LOCALIZATION SETTINGS
+L10N_REPOSITORY ?= https://github.com/rhinstaller/python-simpleline-l10n.git
+L10N_REPOSITORY_RW ?= git@github.com:rhinstaller/python-simpleline-l10n.git
+
+# Branch used in localization repository. This should be master all the time.
+GIT_L10N_BRANCH ?= master
+# Directory in localization repository specific for this branch.
+L10N_DIR ?= master
+
+default: all
+
+.PHONY: all
+all:
+ $(MAKE) -C po
+
+.PHONY: clean
+clean:
+ -rm -rf *.tar.gz simpleline/*.pyc tests/*.pyc ChangeLog dist build simpleline.egg-info
+ $(MAKE) -C po clean
+ $(PYTHON) setup.py -q clean --all
+
+.PHONY: test
+test:
+ @echo "*** Running unittests ***"
+ ./tests/units/run_test.sh
+
+.PHONY: coverage
+coverage:
+ @echo "*** Running unittests with coverage ***"
+ PYTHON="$(COVERAGE) run --branch" ./tests/units/run_test.sh
+ $(COVERAGE) report -m --include="simpleline/*" | tee tests/coverage-report.log
+
+.PHONY: check
+check:
+ @echo "*** Running pylint ***"
+ $(PYTHON) -m pylint simpleline/ examples/*/*.py tests/units/
+
+.PHONY: install
+install:
+ $(PYTHON) setup.py install --root=$(DESTDIR)
+ $(MAKE) -C po install
+
+.PHONY: ChangeLog
+ChangeLog:
+ (GIT_DIR=.git git log > .changelog.tmp && mv .changelog.tmp ChangeLog; rm -f .changelog.tmp) || (touch ChangeLog; echo 'git directory not found: installing possibly empty changelog.' >&2)
+
+.PHONY: tag
+tag:
+ git tag -a -m "Tag as $(TAG)" -f $(TAG)
+ @echo "Tagged as $(TAG)"
+
+.PHONY: release
+release: tag archive
+
+.PHONY: archive
+archive: po-pull ChangeLog
+ $(PYTHON) setup.py $(BUILD_ARGS)
+ @echo "The archive is in dist/$(PKGNAME)-$(VERSION).tar.gz"
+
+.PHONY: rpmlog
+rpmlog:
+ @git log --no-merges --pretty="format:- %s (%ae)" $(TAG).. |sed -e 's/@.*)/)/'
+ @echo
+
+.PHONY: potfile
+potfile:
+ $(MAKE) -C po potfile
+
+.PHONY: po-pull
+po-pull:
+ TEMP_DIR=$$(mktemp --tmpdir -d $(SPECNAME)-localization-XXXXXXXXXX) && \
+ git clone --depth 1 -b $(GIT_L10N_BRANCH) -- $(L10N_REPOSITORY) $$TEMP_DIR && \
+ cp $$TEMP_DIR/$(L10N_DIR)/*.po ./po/ && \
+ rm -rf $$TEMP_DIR
+
+.PHONY: po-push
+po-push: potfile
+# This algorithm will make these steps:
+# - clone localization repository
+# - copy pot file to this repository
+# - check if pot file is changed (ignore the POT-Creation-Date otherwise it's always changed)
+# - if not changed:
+# - remove cloned repository
+# - if changed:
+# - add pot file
+# - commit pot file
+# - tell user to verify this file and push to the remote from the temp dir
+ TEMP_DIR=$$(mktemp --tmpdir -d $(SPECNAME)-localization-XXXXXXXXXX) || exit 1 ; \
+ git clone --depth 1 -b $(GIT_L10N_BRANCH) -- $(L10N_REPOSITORY_RW) $$TEMP_DIR || exit 2 ; \
+ cp ./po/$(SPECNAME).pot $$TEMP_DIR/$(L10N_DIR)/ || exit 3 ; \
+ pushd $$TEMP_DIR/$(L10N_DIR) ; \
+ git difftool --trust-exit-code -y -x "diff -u -I '^\"POT-Creation-Date: .*$$'" HEAD ./$(SPECNAME).pot &>/dev/null ; \
+ if [ $$? -eq 0 ] ; then \
+ popd ; \
+ echo "Pot file is up to date" ; \
+ rm -rf $$TEMP_DIR ; \
+ else \
+ git add ./$(SPECNAME).pot && \
+ git commit -m "Update $(SPECNAME).pot" && \
+ popd && \
+ echo "Pot file updated for the localization repository $(L10N_REPOSITORY)" && \
+ echo "Please confirm changes and push:" && \
+ echo "$$TEMP_DIR" ; \
+ fi ;
+
+.PHONY: bumpver
+bumpver: po-push
+ read -p "Please see the above message. Verify and push localization commit. Press anything to continue." -n 1 -r
+
+ @NEWSUBVER=$$((`echo $(VERSION) |cut -d . -f 2` + 1)) ; \
+ NEWVERSION=`echo $(VERSION).$$NEWSUBVER |cut -d . -f 1,3` ; \
+ DATELINE="* `LC_ALL=C.UTF-8 date "+%a %b %d %Y"` `git config user.name` <`git config user.email`> - $$NEWVERSION-1" ; \
+ cl=`grep -n %changelog $(SPECNAME).spec |cut -d : -f 1` ; \
+ tail --lines=+$$(($$cl + 1)) $(SPECNAME).spec > speclog ; \
+ (head -n $$cl $(SPECNAME).spec ; echo "$$DATELINE" ; make --quiet rpmlog 2>/dev/null ; echo ""; cat speclog) > $(SPECNAME).spec.new ; \
+ mv $(SPECNAME).spec.new $(SPECNAME).spec ; rm -f speclog ; \
+ sed -i "s/Version: $(VERSION)/Version: $$NEWVERSION/" $(SPECNAME).spec ; \
+ sed -i "s/version='$(VERSION)'/version='$$NEWVERSION'/" setup.py
+
+.PHONY: pypi-upload
+pypi-upload:
+ $(PYTHON) -m twine upload dist/*
+
+.PHONY: ci
+ci: check test
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/README.md b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/README.md
new file mode 100644
index 0000000..e1de238
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/README.md
@@ -0,0 +1,48 @@
+[](https://copr.fedorainfracloud.org/coprs/g/rhinstaller/Anaconda/package/python-simpleline/)
+[](https://translate.fedoraproject.org/engage/python-simpleline/?utm_source=widget)
+
+Python Simpleline
+=================
+
+This is a text UI framework originally which was a part of the
+[Anaconda](https://github.com/rhinstaller/anaconda) installer project.
+
+This UI is simple and easy to use. It is designed to be used with line-based
+machines and tools (e.g. serial console) so that every new line is appended
+to the bottom of the screen. Printed lines are never rewritten!
+
+How to
+======
+
+[Link](https://python-simpleline.readthedocs.io) to the documentation.
+
+Another way to learn how to use this library is to view the
+[examples directory](https://github.com/rhinstaller/python-simpleline/tree/master/examples).
+Examples can be run without installing Simpleline to the system. You can test
+them by running the script `run_example.sh`.
+
+1. `cd examples`
+2. `./run_example.sh example_folder_name`
+
+For example:
+`./run_example.sh basic`
+
+Dependencies
+============
+
+This is a Python3-only project. This code should not be difficult to migrate to
+Python2. However, there is no need from the community, so it is only compatible
+with Python3 at the moment.
+No special libraries are required to use this library. If you want to use glib
+event loop instead of the original one you need to install glib and Python3
+gobject introspection.
+
+If you want to run tests (`make ci`), you need to install
+[Pocketlint](https://github.com/rhinstaller/pocketlint) and glib with gobject
+introspection for Python3.
+
+Note
+====
+
+Thanks to Martin Sivák for the original Anaconda project. It was really nice
+starting place for the new Simpleline's form.
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/doc/Makefile b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/doc/Makefile
new file mode 100644
index 0000000..8f7141e
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/doc/Makefile
@@ -0,0 +1,20 @@
+# Minimal makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS =
+SPHINXBUILD = sphinx-build-3
+SPHINXPROJ = Simpleline
+SOURCEDIR = .
+BUILDDIR = _build
+
+# Put it first so that "make" without argument is like "make help".
+help:
+ @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+
+.PHONY: help Makefile
+
+# Catch-all target: route all unknown targets to Sphinx using the new
+# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
+%: Makefile
+ @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/doc/api.rst b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/doc/api.rst
new file mode 100644
index 0000000..ee79e6e
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/doc/api.rst
@@ -0,0 +1,22 @@
+.. _public_api_label:
+
+Public API
+==========
+
+API listed here is the public API. Developers shouldn't use anything which is not mentioned here!
+
+.. toctree::
+ :maxdepth: 1
+ :caption: Contents:
+
+ App
+ UIScreen
+ Screen Handling
+ Widgets
+ Containers
+ Prompt
+ Advanced Widgets
+ Event loops
+ Signals
+ Errors
+ Advanced Input
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/doc/api/adv_widgets.rst b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/doc/api/adv_widgets.rst
new file mode 100644
index 0000000..62a8d0b
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/doc/api/adv_widgets.rst
@@ -0,0 +1,13 @@
+Advanced Widgets
+================
+
+Advanced widgets are :class:`screens ` which can be used for a
+specific purpose. For example, reading input from the user and testing acceptance conditions on this
+input, or asking the user a yes/no question.
+
+Advanced widget classes
+-----------------------
+
+.. automodule:: simpleline.render.adv_widgets
+ :members:
+ :show-inheritance:
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/doc/api/advanced_input.rst b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/doc/api/advanced_input.rst
new file mode 100644
index 0000000..d58fad1
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/doc/api/advanced_input.rst
@@ -0,0 +1,92 @@
+Advanced Input
+==============
+
+.. automodule:: simpleline.input.input_handler
+
+.. WARNING::
+ This section contains advanced input techniques which may lead to buggy code in your
+ program if they are not used correctly.
+
+The default technique to get user input is already described in :ref:`UIScreen `,
+and that should be the preferred way to obtain user input. However, if the default technique is
+not enough for your situation then read the text below to find out how to implement custom input.
+
+Input handler classes
+---------------------
+Input handler classes are classes created to obtain user input. If required
+:class:`InputHandler` can block an application until user input is received.
+
+This class can be instantiated everywhere in the code and used to ask for user input::
+
+ handler = InputHandler()
+ handler.get_input("Shut up and give me your input:")
+ handler.wait_for_input()
+ if handler.input_successful:
+ user_input = handler.value
+
+The :meth:`InputHandler.wait_on_input` method will block code processing until user input is
+received. Input should always be checked before processing.
+
+In case some other work needs to be done before user input is received, then pass
+a callback to the constructor of the :class:`InputHandler` class and do not use
+the :meth:`InputHandler.wait_on_input` method. However, the callback is using
+the :ref:`event loop` so it won't be called until event loop
+processing is active. If :ref:`concurrent input` is used then this
+callback might never get called!
+
+If what a user types must not be displayed, then the :class:`PasswordInputHandler` class
+should be used. It shares most of its implementation with the :class:`InputHandler` but overrides
+how to obtain the code. For more info look at the :class:`PasswordInputHandler` class
+documentation.
+
+.. _concurrent_input_label:
+
+Concurrent input
+----------------
+
+Concurrent input is something which should be avoided. It drags unexpected behavior into an
+application and is hard to debug. However, there could be an instance when user input is required
+immediately, even when an application is already waiting for other input.
+
+By default, every attempt for concurrent input will raise an exception and kill the application to
+prevent unexpected behavior. In order to allow for concurrent input, the
+:attr:`InputHandler.skip_concurrency_check` property must be set. After this property is disabled
+for the :class:`InputHandler` instance, then the handler instance then it can support
+concurrent input.
+
+The last registered concurrent input will result in dropping all other waiting inputs -- even other
+waiting inputs with :attr:`InputHandler.skip_concurrency_check` will be dropped. The dropped
+waiting inputs will get a failed input signal to unblock :meth:`InputHandler.wait_on_input`
+methods.
+
+
+Creating a custom InputHandler
+------------------------------
+
+If the :class:`InputHandler` class or the :class:`PasswordInputHandler` class is not enough,
+developers can create their own handler. The structure of an input handler is based on two
+classes. First is a handler itself, and second is the request object it creates.
+
+The handler class is used as an interface for the rest of the application. It also creates
+the requester instance. The requester object is a low-level implementation of obtaining user input.
+The requester object has to have the ``get_input`` method and should contain the ``text_prompt``
+method. The ``get_input`` method is called in a separate thread and should prompt the user for
+input. The ``text_prompt`` method is required mainly for concurrent input, and it returns
+a string representation of the prompt.
+
+For more details, please look at the implementation of the :class:`InputHandler` class.
+
+
+InputHandler class
+------------------
+
+.. autoclass:: InputHandler
+ :members:
+
+PasswordInputHandler class
+--------------------------
+
+.. autoclass:: PasswordInputHandler
+ :members:
+ :inherited-members:
+
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/doc/api/app.rst b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/doc/api/app.rst
new file mode 100644
index 0000000..f0b058d
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/doc/api/app.rst
@@ -0,0 +1,48 @@
+App
+===
+
+.. automodule:: simpleline
+
+The App class is the heart of Simpleline. It holds the :ref:`event loop `
+instance and scheduler which are used by Simpleline. Both can be replaced if needed in the
+initialization phase by calling the :meth:`App.initialize` method. However, by replacing the
+scheduler you are replacing most of the logic in Simpleline. Replacing the scheduler is not
+supported, since it is currently not a part of the public API.
+
+All Simpleline applications must be run by the :meth:`App.run` method. This method will start
+the event loop instance created in the initialization process.
+
+If a reaction on application quit is required, please set
+:meth:`set_quit_callback ` in the used event loop.
+This can be done by::
+
+ loop = App.get_event_loop()
+ loop.set_quit_callback(callback_function)
+
+**Do not instantiate the** :class:`App` **class!** It is designed to be used in a purely static way.
+
+
+Application configuration
+-------------------------
+
+Configuration of the application is saved in the
+:class:`GlobalConfiguration ` class. This class can be
+created before :meth:`App.initialize` is called and passed in as a
+parameter. This way the same configuration can be used between re-initialization of the
+application. The configuration can easily be changed, even while an application is running,
+by setting desired properties.
+
+Look at the :class:`GlobalConfiguration ` to find out
+all the configuration possibilities.
+
+App class
+---------
+
+.. autoclass:: App
+ :members:
+
+GlobalConfiguration class
+-------------------------
+
+.. autoclass:: simpleline.global_configuration.GlobalConfiguration
+ :members:
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/doc/api/containers.rst b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/doc/api/containers.rst
new file mode 100644
index 0000000..d15aad1
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/doc/api/containers.rst
@@ -0,0 +1,47 @@
+.. _containers_label:
+
+Containers
+==========
+
+Containers are structures to hold widgets and handle automatic positioning. Containers are
+essentially widgets containing other widgets. Below is a collection of default containers which
+can position :ref:`widgets `, but they can do more (e.g. handle user input).
+Recursive composition of containers is supported.
+
+Customized containers can also be created. See the :ref:`creating_a_custom_container_label`
+section.
+
+Container classes
+-----------------
+
+.. automodule:: simpleline.render.containers
+ :members:
+ :inherited-members:
+ :show-inheritance:
+
+.. _creating_a_custom_container_label:
+
+Creating a custom container
+---------------------------
+
+If an existing container is missing a required feature, a new, customized container can be
+created based on the :class:`Container` class. Container creation is essentially the same as
+:ref:`custom widget creation ` because containers are based on widgets.
+The main difference is that containers are working with widgets added by a developer when using
+the container. As an example, every :class:`UIScreen ` has a
+:class:`WindowContainer`, and this is used as the main rendering point.
+
+To create a customized container, the :meth:`Container.render` method should be overridden and
+the positioning of widgets and containers should be done here. It can even enhance these widgets,
+for example, by adding numbering (this is done in :class:`ListRowContainer` or
+:class:`ListColumnContainer`). The :meth:`Container.render` method should call the
+:meth:`Container.draw` method. The :meth:`Container.draw` method should be called for every
+widget placed. For a better understanding please refer to the existing implementation.
+
+Base container class
+--------------------
+
+.. autoclass:: Container
+ :members:
+ :inherited-members:
+ :show-inheritance:
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/doc/api/errors.rst b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/doc/api/errors.rst
new file mode 100644
index 0000000..ebf7111
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/doc/api/errors.rst
@@ -0,0 +1,39 @@
+Errors module
+=============
+
+.. automodule:: simpleline.errors
+
+Collection of generic exception classes used everywhere in the project. The most important one is
+:class:`SimplelineError`, which is the base exception for all the other exceptions used in
+the Simpleline project.
+
+.. autoexception:: SimplelineError
+ :members:
+ :show-inheritance:
+
+Render exceptions
+-----------------
+
+Exceptions used for rendering errors.
+
+
+.. automodule:: simpleline.render
+
+.. autoexception:: RenderError
+ :members:
+ :show-inheritance:
+
+.. autoexception:: RenderUnexpectedError
+ :members:
+ :show-inheritance:
+
+Event loop exceptions
+---------------------
+
+Exceptions used for errors in event loops.
+
+.. automodule:: simpleline.event_loop
+
+.. autoexception:: simpleline.event_loop.ExitMainLoop
+ :members:
+ :show-inheritance:
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/doc/api/event_loops.rst b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/doc/api/event_loops.rst
new file mode 100644
index 0000000..35503e5
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/doc/api/event_loops.rst
@@ -0,0 +1,92 @@
+.. _event_loops_label:
+
+Event Loops
+===========
+
+.. currentmodule:: simpleline.event_loop
+
+Event loops are the heart of Simpleline. Every event loop is based on the
+:class:`AbstractEventLoop`, and they all work with :ref:`signals `.
+A signal is a message passed to the loop containing some information. Signals are passed to
+an event loop by calling :meth:`AbstractEventLoop.enqueue_signal`. These signals are then
+processed by calling :meth:`AbstractEventLoop.process_signals`. This method can be called by
+an application developer manually or by the :meth:`AbstractEventLoop.run` method, which is
+called by :meth:`App.run() ` to start the Simpleline-based application.
+When a signal is processed, all handlers attached to this signal are called. Signal handler
+assignment is done by the :meth:`AbstractEventLoop.register_signal_handler` method.
+
+Event loops can also be started recursively by :meth:`AbstractEventLoop.execute_new_loop`.
+The old event loop is waiting for this event loop to stop. New loop execution is mandatory for
+modal screens to work, since they can't be interrupted by other screens. This new event
+loop is terminated by closing the last screen in the event loop or by calling
+the :meth:`AbstractEventLoop.close_loop` method.
+
+The last event loop should be terminated by closing the last screen in the screen stack or by
+calling :meth:`AbstractEventLoop.close_loop`. In case a fatal error occurs the
+:meth:`AbstractEventLoop.force_quit` method can be used to immediately kill the loop.
+
+If a reaction on quitting the application (closing the last event loop) is required, the quit
+callback can be used. The quit callback can be set by
+the :meth:`AbstractEventLoop.set_quit_callback` method.
+
+The following event loops are supported by Simpleline, but you can also
+:ref:`Create_your_own_loop_label` :
+
+* :ref:`MainLoop_label`
+* :ref:`GLib_Event_loop_label`
+
+.. _MainLoop_label:
+
+Main Loop
+---------
+
+The main loop is the default event loop for Simpleline projects. The benefit of using
+:class:`MainLoop ` is that it isn't necessary to have any dependencies on
+other libraries. It is a lightweight event loop implemented completely in Python.
+
+.. autoclass:: simpleline.event_loop.main_loop.MainLoop
+ :members:
+ :inherited-members:
+ :show-inheritance:
+
+.. _GLib_Event_loop_label:
+
+GLib Event loop
+---------------
+
+The GLib event loop was added in order to utilize existing event loops used by other libraries,
+for example, DBus connections. Simpleline with this loop should have the same behavior as with
+the :ref:`MainLoop_label`.
+
+To use this loop you need to set it via the :class:`App ` class::
+
+ # Create Glib event loop.
+ glib_loop = GLibEventLoop()
+
+ # Use glib event loop instead of the original one.
+ # Everything else should behave the same as with the original Simpleline loop.
+ App.initialize(event_loop=glib_loop)
+
+The GLib loop can be accessed by the
+:attr:`GLibEventLoop.active_main_loop `
+property, or by getting the default loop from GLib directly
+`GLib.MainLoop() `_.
+
+.. autoclass:: simpleline.event_loop.glib_event_loop.GLibEventLoop
+ :members:
+ :inherited-members:
+ :show-inheritance:
+
+.. _Create_your_own_loop_label:
+
+Create your own loop
+--------------------
+
+If new loop support is required, it should inherit from :class:`AbstractEventLoop` and
+implement the same behavior as the :ref:`MainLoop_label`. You can use existing tests from the
+event loops to start. If the new loop is stable enough, pull requests are always welcome at
+`Simpleline repository `_.
+
+.. autoclass:: simpleline.event_loop.AbstractEventLoop
+ :members:
+ :inherited-members:
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/doc/api/prompt.rst b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/doc/api/prompt.rst
new file mode 100644
index 0000000..eaf9e9c
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/doc/api/prompt.rst
@@ -0,0 +1,23 @@
+.. _prompt_label:
+
+Prompt
+======
+
+.. automodule:: simpleline.render.prompt
+
+Class for prompting a user for input. New user options can be added by :meth:`Prompt.add_option`,
+removed by :meth:`Prompt.remove_option` or updated by :meth:`Prompt.update_option`.
+A message for the user can also be set by the :meth:`Prompt.set_message` method.
+
+This class is used in the :class:`UIScreen ` class. The default
+instance always handles *r* (refresh), *c* (continue) and *q* (quit) and is created in the
+:meth:`UIScreen.prompt() ` method. To create your own
+custom prompt please override
+the :meth:`UIScreen.prompt() ` method.
+
+Prompt class
+------------
+
+.. autoclass:: Prompt
+ :members:
+ :inherited-members:
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/doc/api/screen.rst b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/doc/api/screen.rst
new file mode 100644
index 0000000..5b0da9d
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/doc/api/screen.rst
@@ -0,0 +1,126 @@
+.. _uiscreen_label:
+
+UIScreen
+========
+
+.. automodule:: simpleline.render.screen
+
+The base class for creating a new screen. :class:`UIScreen` is used for any user interaction.
+:class:`UIScreen` uses :ref:`containers ` and :ref:`widgets `
+to present information to a user, and the :meth:`UIScreen.input` method to get input from a user
+to an application. Screens are pushed to the screen stack, which are then used to
+communicate with a user. See the :ref:`screen handling ` section to find
+out more.
+
+The methods :meth:`UIScreen.redraw`, :meth:`UIScreen.close`, :meth:`UIScreen.emit` and
+:meth:`UIScreen.create_and_emit` are asynchronous. These methods will create a signal which
+is passed to the event loop for later processing.
+
+**Beware**, methods :meth:`UIScreen.redraw` and :meth:`UIScreen.close` can lead to unexpected
+behavior when multiple instances of these signals are emitted (methods are called multiple times).
+This can even crash your application.
+
+Lifecycle
+---------
+
+Every :class:`UIScreen` has a distinct lifecycle: *uninitialized*, *initialized*, *draw*,
+*process input* and *closed*. If the screen has already been initialized
+(it was *drawn* to a monitor) and will be shown again, then the screen skips
+the *uninitialized* stage. The screen can cycle between *draw* and *process input* stages by
+calling the :meth:`UIScreen.redraw` method after processing user input.
+
+In case :meth:`UIScreen.redraw` won't be called and no new screen is pushed, or this screen
+wasn't closed, then an application will stay in an infinite loop waiting for something to happen.
+This is correct behavior because there could be something in an event loop which will
+call :meth:`UIScreen.redraw` later.
+
+Rendering widgets
+-----------------
+
+The :meth:`UIScreen.refresh` method is the most important part of the :class:`UIScreen`.
+It contains preparations for rendering (creating widgets and adding them to containers).
+The :meth:`UIScreen.refresh` method will be called before anything is drawn on a monitor.
+
+The :attr:`UIScreen.window` attribute, which is the
+:class:`WindowContainer ` instance, contains
+all items (widgets, containers) which are to be rendered by the screen.
+A new :class:`WindowContainer ` is created in the
+:meth:`UIScreen.refresh` method for every screen redraw. The :attr:`UIScreen.title` attribute is
+passed to the :attr:`WindowContainer.title `
+property, and a developer can add :ref:`widgets ` and other
+:ref:`containers ` to present items to a user by calling
+:meth:`WindowContainer.add() ` or
+:meth:`WindowContainer.add_with_separator() `.
+Multiple items can be added by calling these methods repeatedly.
+
+When everything is prepared properly in the :meth:`UIScreen.refresh` method, it needs to be drawn
+on the monitor for a user. This is handled by the :meth:`UIScreen.show_all` method. This method
+works automatically, but it could also be useful for a developer, especially when the screen will
+not process input. In this case, the :meth:`UIScreen.input` method is not called at all.
+Developers can override this method and call the parent class's :meth:`UIScreen.show_all`,
+which will handle drawing the screen and any additional processing.
+
+Redrawing the screen can be invoked by the :meth:`UIScreen.redraw` method. However, this is
+not processed immediately. Instead it will be added to the event loop and processed later, when the
+loop is idle. Beware, after every :meth:`redraw ` call the input is processed
+if not disabled, so if multiple redraw signals are emitted, than the application will
+crash.
+
+The :meth:`UIScreen.redraw` method is also invoked when a screen is
+:ref:`pushed ` to the stack.
+
+User input processing
+---------------------
+
+If the screen shouldn't process user input, the :attr:`UIScreen.input_required` property needs
+to be set to `False`. `True` is the default value for this property.
+
+After everything is printed to a monitor, the :class:`UIScreen` will wait for user input.
+For this purpose there is :meth:`UIScreen.input`, which is called when a user passes string input
+to a screen. The screen needs to react upon user input and return one of the options from the
+:class:`InputState` enum or the user input string.
+
+To accept the user input, :attr:`InputState.PROCESSED`, :attr:`InputState.PROCESSED_AND_REDRAW` or
+:attr:`InputState.PROCESSED_AND_CLOSE` should be returned. Addition to accepting user input the
+:attr:`InputState.PROCESSED_AND_REDRAW` value will also redraw active screen and
+:attr:`InputState.PROCESSED_AND_CLOSE` will close active screen. However, if
+:attr:`InputState.PROCESSED` is used then the developer is responsible for not ending in frozen
+application. The :meth:`UIScreen.refresh` or the :meth:`UIScreen.close` methods must be called
+manually.
+
+In case the user input is invalid, the :attr:`InputState.DISCARDED` value should be returned.
+This will reject the user input and wait for another attempt. The :class:`UIScreen.refresh`
+method will be called after 5 rejections and show the screen output again.
+
+If the user input string is returned it will be checked for
+options of the :class:`Prompt ` instance which can either
+close the screen, refresh the screen (this will call :meth:`UIScreen.refresh`)
+or quit the application.
+
+Closing screens
+---------------
+
+There are several ways to close a screen. One is by calling :meth:`UIScreen.close` or
+by pressing *c* to continue (with the default
+:class:`Prompt ` class). When the screen is closed
+the next screen on the stack will be shown. A screen can also be
+:ref:`replaced `. Then the original screen is removed from the stack
+without closing a screen. If a reaction on closing a screen is required then the
+:meth:`UIScreen.closed` callback should be overridden.
+
+**Beware**, when calling :meth:`UIScreen.close` multiple times it will close multiple screens.
+This is because the close signal will always close the top screen on the screen stack.
+
+UIScreen class
+--------------
+
+.. autoclass:: UIScreen
+ :members:
+ :inherited-members:
+
+InputState enum
+---------------
+
+.. autoclass:: InputState
+ :members:
+ :undoc-members:
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/doc/api/screen_handling.rst b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/doc/api/screen_handling.rst
new file mode 100644
index 0000000..561ed52
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/doc/api/screen_handling.rst
@@ -0,0 +1,82 @@
+.. _screen_handling_label:
+
+Screen Handling
+===============
+
+.. automodule:: simpleline.render.screen_handler
+
+The :class:`ScreenHandler` class is used to schedule a
+:class:`UIScreen ` to the screen stack.
+
+Screen handling is an important part of using the Simpleline library, and it is recommended
+for a developer to get familiar with this principle. Screen handling is the gateway by which
+a developer manages the stack, by either adding or removing screens from it.
+There are many ways to add a screen to the stack. To remove a screen from the stack, the screen
+must be closed, or it can be replaced by another screen.
+To close a :class:`UIScreen ` a developer should call
+the :meth:`UIScreen.close() `
+method. A screen can also be closed when a user presses `c` (which can be disabled). This will
+close the screen automatically. When a screen is closed, the next screen on the top of
+the stack will be rendered. If the stack is empty then the application will close.
+
+**Do not instantiate the** :class:`ScreenHandler` **class!** All methods in the
+:class:`ScreenHandler` class are class methods so the :class:`ScreenHandler` shouldn't be
+instantiated at all.
+
+The following operations can be used to schedule a screen.
+
+Schedule screen
+---------------
+
+To schedule a screen use the :meth:`ScreenHandler.schedule_screen` method.
+Scheduling a screen should be used on the first screen in your application before starting the
+event loop. The screen is added to the bottom of the screen stack, and it will be visible as
+the last screen in a stack.
+
+This is the only way to add a screen to the screen stack without emitting a redraw call.
+
+.. _push_screen_label:
+
+Push screen
+-----------
+
+To push a screen to the stack use the :meth:`ScreenHandler.push_screen` method.
+Pushing a screen to the stack will place a screen on top of the stack, so it will be
+drawn on the next redraw call. The original screen will remain on the stack, so after the new
+screen is closed, the original screen will be rendered again. If you need to avoid this behavior
+please close the active screen before pushing a new screen to the stack.
+
+The redraw signal will be emitted automatically when a screen is pushed.
+
+.. _push_screen_modal_label:
+
+Push screen as modal
+--------------------
+
+To push a modal screen to the stack use the :meth:`ScreenHandler.push_screen_modal` method.
+This behaves the same as :ref:`push_screen_label` with one important difference. The pushed screen
+will act as a modal screen. A modal screen has its own event loop, so event processing of the old
+loop is blocked until the modal screen is closed. The code processing is also blocked by the
+:meth:`ScreenHandler.push_screen_modal` method call.
+
+The :meth:`UIScreen.redraw() ` method may be
+required if the original screen was already drawn before invoking the
+:meth:`ScreenHandler.push_screen_method` method.
+
+The redraw signal will be emitted automatically when a screen is pushed.
+
+.. _replace_screen_label:
+
+Replace screen
+--------------
+
+Replace an existing screen with a new screen. This behaves like :ref:`push_screen_label` but it
+replaces the original screen, so there is no need to close the original screen.
+
+The redraw signal will be emitted automatically when a screen is replaced.
+
+ScreenHandler class
+-------------------
+
+.. autoclass:: ScreenHandler
+ :members:
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/doc/api/signals.rst b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/doc/api/signals.rst
new file mode 100644
index 0000000..b5f4931
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/doc/api/signals.rst
@@ -0,0 +1,22 @@
+.. _signals_label:
+
+Signals
+=======
+
+This is a collection of signals that can be used in the
+:class:`event loop `.
+
+.. automodule:: simpleline.event_loop.signals
+ :members:
+ :inherited-members:
+ :show-inheritance:
+
+Creating custom signals
+-----------------------
+
+New signals can be created by subclassing an existing signal class or
+:class:`AbstractSignal `
+
+.. autoclass:: simpleline.event_loop.AbstractSignal
+ :members:
+ :inherited-members:
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/doc/api/widgets.rst b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/doc/api/widgets.rst
new file mode 100644
index 0000000..7f48fe6
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/doc/api/widgets.rst
@@ -0,0 +1,44 @@
+.. _widgets_label:
+
+Widgets
+=======
+
+.. currentmodule:: simpleline.render.widgets
+
+Widgets are the basic units to render items on a
+:class:`screen `. Widgets can wrap a common text
+(:class:`TextWidget`) or create empty lines (:class:`SeparatorWidget`). They can also be more
+complex structures (:class:`CheckboxWidget`). A new widget can also be created.
+See :ref:`create_custom_widget_label`.
+
+These widgets should be used in :ref:`containers_label`.
+
+Widgets classes
+---------------
+
+.. automodule:: simpleline.render.widgets
+ :members:
+ :inherited-members:
+ :show-inheritance:
+
+.. _create_custom_widget_label:
+
+Creating a custom widget
+------------------------
+
+To create a custom widget you should subclass the :class:`Widget` class. The most important method
+for creating a customized widget is :meth:`Widget.render`. This method is responsible for
+presenting a user textual information.
+
+If the new widget is composed of other widgets the :meth:`Widget.draw` method should be
+called on every widget used. The :meth:`Widget.write` method should be called if the input
+is a string. These calls can be repeated multiple times. For an example please look at the
+existing implementation.
+
+Base Widget class
+-----------------
+
+.. autoclass:: Widget
+ :members:
+ :inherited-members:
+ :show-inheritance:
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/doc/conf.py b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/doc/conf.py
new file mode 100644
index 0000000..c6941bc
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/doc/conf.py
@@ -0,0 +1,214 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# Simpleline documentation build configuration file, created by
+# sphinx-quickstart on Fri Nov 3 11:00:41 2017.
+#
+# This file is execfile()d with the current directory set to its
+# containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#
+import os
+import sys
+from unittest.mock import MagicMock
+
+
+sys.path.insert(0, os.path.abspath('..'))
+
+
+# -- General configuration ------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#
+# needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = ['sphinx.ext.autodoc',
+ 'sphinx.ext.doctest',
+ 'sphinx.ext.intersphinx',
+ 'sphinx.ext.todo',
+ 'sphinx.ext.coverage',
+ 'sphinx.ext.imgmath']
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix(es) of source filenames.
+# You can specify multiple suffix as a list of string:
+#
+# source_suffix = ['.rst', '.md']
+source_suffix = '.rst'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = 'Simpleline'
+copyright = '2017, Jiri Konecny' # pylint: disable=redefined-builtin
+author = 'Jiri Konecny'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+
+
+def get_version():
+ """Read version from ../python-simpleline.spec ."""
+ import re
+ version_re = re.compile(r"^Version: *([\d.]+)$")
+ with open("../python-simpleline.spec", "r") as f:
+ for line in f:
+ m = version_re.match(line)
+ if m:
+ return m.group(1)
+
+
+# The short X.Y version.
+version = get_version()
+# The full version, including alpha/beta/rc tags.
+release = version
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#
+# This is also used if you do content translation via gettext catalogs.
+# Usually you set "language" from the command line for these cases.
+language = None
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+# This patterns also effect to html_static_path and html_extra_path
+exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+# If true, `todo` and `todoList` produce output, else they produce nothing.
+todo_include_todos = True
+
+
+# -- Options for HTML output ----------------------------------------------
+
+# The theme to use for HTML and HTML Help pages. See the documentation for
+# a list of builtin themes.
+#
+html_theme = 'alabaster'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further. For a list of options available for each theme, see the
+# documentation.
+#
+# html_theme_options = {}
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+
+# -- Options for HTMLHelp output ------------------------------------------
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'Simplelinedoc'
+
+
+# -- Options for LaTeX output ---------------------------------------------
+
+latex_elements = {
+ # The paper size ('letterpaper' or 'a4paper').
+ #
+ # 'papersize': 'letterpaper',
+
+ # The font size ('10pt', '11pt' or '12pt').
+ #
+ # 'pointsize': '10pt',
+
+ # Additional stuff for the LaTeX preamble.
+ #
+ # 'preamble': '',
+
+ # Latex figure (float) alignment
+ #
+ # 'figure_align': 'htbp',
+}
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title,
+# author, documentclass [howto, manual, or own class]).
+latex_documents = [
+ (master_doc, 'Simpleline.tex', 'Simpleline Documentation',
+ 'Jiri Konecny', 'manual'),
+]
+
+
+# -- Options for manual page output ---------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+ (master_doc, 'simpleline', 'Simpleline Documentation',
+ [author], 1)
+]
+
+
+# -- Options for Texinfo output -------------------------------------------
+
+# Grouping the document tree into Texinfo files. List of tuples
+# (source start file, target name, title, author,
+# dir menu entry, description, category)
+texinfo_documents = [
+ (master_doc, 'Simpleline', 'Simpleline Documentation',
+ author, 'Simpleline', 'One line description of project.',
+ 'Miscellaneous'),
+]
+
+
+
+# -- Options for Epub output ----------------------------------------------
+
+# Bibliographic Dublin Core info.
+epub_title = project
+epub_author = author
+epub_publisher = author
+epub_copyright = copyright
+
+# The unique identifier of the text. This can be a ISBN number
+# or the project homepage.
+#
+# epub_identifier = ''
+
+# A unique identification for the text.
+#
+# epub_uid = ''
+
+# A list of files that should not be packed into the epub file.
+epub_exclude_files = ['search.html']
+
+
+
+# Example configuration for intersphinx: refer to the Python standard library.
+intersphinx_mapping = {'https://docs.python.org/3': None}
+
+
+# -- Mock missing stuff in readthedocs ------------------------------------
+
+
+class Mock(MagicMock):
+ @classmethod
+ def __getattr__(cls, name):
+ return MagicMock()
+
+
+MOCK_MODULES = ['gi', 'gi.repository']
+sys.modules.update((mod_name, Mock()) for mod_name in MOCK_MODULES)
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/doc/guide.rst b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/doc/guide.rst
new file mode 100644
index 0000000..ea3ed3b
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/doc/guide.rst
@@ -0,0 +1,176 @@
+.. _guide_label:
+
+Guide to Simpleline
+===================
+
+Simpleline is a text user interface framework written completely in Python 3 with a possibility to
+have :ref:`non-python event loops `. With the exception of optional event loops,
+Simpleline has almost no dependency on external libraries.
+
+This UI is simple and easy to use. It is designed to be used with line-based machines and tools
+(e.g. serial console) so that every new line is appended to the bottom of the screen.
+Printed lines are never rewritten!
+
+Basic components
+----------------
+
+For every application, the following parts are always required.
+
+* The :class:`App ` static class to initialize and run application.
+* The :class:`ScreenHandler ` static class for
+ scheduling screens.
+* The :class:`UIScreen ` based classes to create screens which
+ will form the application.
+* :ref:`Widgets ` to show anything on the screens.
+* :ref:`Containers ` to position widgets on the screen.
+
+Look at the next section to see how everything fits together.
+
+How to create a simple application
+----------------------------------
+
+.. currentmodule:: simpleline.render.screen
+
+Interaction with a user is necessary to have a useful UI framework. To show anything to a user the
+:class:`UIScreen` class must be used. So we will subclass this class to create our screen and set
+a title for it::
+
+ class DividerScreen(UIScreen):
+
+ def __init__(self):
+ # Set title of the screen.
+ super().__init__(title=u"Divider")
+ self._message = 0
+
+The ``self._message`` variable will be used later to show results to the user.
+
+The screen's main purpose is to present content to a user. For this we need widgets and containers.
+
+The :attr:`UIScreen.window` attribute is the most important part of the screen for rendering.
+It contains the :class:`WindowContainer `
+container which is created and filled up by the :meth:`UIScreen.refresh` method.
+Everything added to this container is printed to the monitor.
+We should override the :meth:`UIScreen.refresh` method and call the parent's version to
+prepare the container. Then we can add :ref:`widgets ` to the container.
+The screen will continue like this:
+
+.. code-block:: python
+ :dedent: 0
+
+ def refresh(self, args=None):
+ # Fill the self.window attribute by the WindowContainer and set screen title as header.
+ super().refresh()
+
+ widget = TextWidget("Result: " + str(self._message))
+ self.window.add_with_separator(widget)
+
+The :meth:`WindowContainer.add_with_separator() `
+method will print a blank line after the
+:class:`TextWidget's ` text.
+
+Now the user has the header and result printed on the screen but it would be nice to give them a
+hint about how to use the Divider screen. The best part of the screen for this is the
+:class:`Prompt `.
+The :class:`Prompt ` class is responsible for guiding the user
+by giving them a set of possible options to choose from.
+
+We will set the message of the prompt inside of the :meth:`UIScreen.prompt` method. We also
+remove the default option to continue, because it functions the same as quitting when there is
+only one screen in the application.
+
+.. code-block:: python
+ :dedent: 0
+
+ def prompt(self, args=None):
+ # Change user prompt
+ prompt = super().prompt()
+
+ # Set message to the user prompt. Give a user hint how he/she may control our application.
+ prompt.set_message("Pass numbers to divider in a format: 'num / num'")
+
+ # Remove continue option from the control. There is no need for that
+ # when we have only one screen.
+ prompt.remove_option('c')
+
+ return prompt
+
+When we are able to present our content to a user, we want to have the possibility to process
+user input. For this purpose there is the :meth:`UIScreen.input` method. Input from a user is
+passed to this method, and the screen may process it, discard it or return it for further
+processing.
+
+If input processing is not required and the screen should only be used for displaying
+information to a user, then the :attr:`UIScreen.input_required` property should be set to `False`.
+However, in this case you need to :meth:`close `, :meth:`redraw `
+or :ref:`push ` a new screen manually. This can be done, for example, in the
+:meth:`UIScreen.show_all` method.
+
+.. code-block:: python
+ :dedent: 0
+
+ def input(self, args, key):
+ """Process input from user and catch numbers with '/' symbol."""
+
+ # Test if user passed valid input for divider.
+ # This will basically take number + number and nothing else and only positive numbers.
+ groups = re.match(r'(\d+) *\/ *(\d+)$', key)
+ if groups:
+ num1 = int(groups[1])
+ num2 = int(groups[2])
+
+ # Dividing by zero is not valid so we won't accept this input from the user. New
+ # input is then required from the user.
+ if num2 == 0:
+ return InputState.DISCARDED
+
+ self._message = int(num1 / num2)
+
+ # Because this input is processed we need to show this screen (show the result)
+ # again by returning PROCESSED_AND_REDRAW.
+ # This will call the refresh method so our new result will be processed inside
+ # of the refresh() method.
+ return InputState.PROCESSED_AND_REDRAW
+ else:
+ # Not input for our screen, try other default inputs. This will result in the
+ # same state as DISCARDED when no default option is used.
+ return key
+
+
+.. py:currentmodule:: simpleline
+
+Our screen is finished. Next, we need to use it in our application. To run an application the
+:class:`App` static class must be used.
+
+This class will initialize an event loop and the scheduler by the :meth:`App.initialize` method.
+When the application is initialized, we need to pass our screen to the screen stack (you can
+pass multiple screens but we have only one here). To pass a screen to the screen stack we will use
+:class:`ScreenHandler `. For further explanation
+on screen scheduling, refer to the :ref:`screen_handling_label` section.
+
+.. code-block:: python
+ :dedent: 0
+
+ if __name__ == "__main__":
+ # Initialize application (create scheduler and event loop).
+ App.initialize()
+
+ # Create our screen.
+ screen = DividerScreen()
+
+ # Schedule screen to the screen scheduler.
+ # This can be called only after App.initialize().
+ ScreenHandler.schedule_screen(screen)
+
+ # Run the application. You must have some screen scheduled
+ # otherwise it will end in an infinite loop.
+ App.run()
+
+Well done! You have your first application in Simpleline.
+
+Further reading
+---------------
+
+I would recommend everyone who wants to use Simpleline to look at the
+`examples `_ which is one
+of the best sources of information. Another place to look is the :ref:`public_api_label`
+documentation section.
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/doc/index.rst b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/doc/index.rst
new file mode 100644
index 0000000..e03ca11
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/doc/index.rst
@@ -0,0 +1,23 @@
+.. Simpleline documentation master file, created by
+ sphinx-quickstart on Fri Nov 3 11:00:41 2017.
+ You can adapt this file completely to your liking, but it should at least
+ contain the root `toctree` directive.
+
+Welcome to Simpleline's documentation!
+======================================
+
+.. toctree::
+ :maxdepth: 1
+ :caption: Contents:
+
+ introduction
+ guide
+ api
+
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/doc/introduction.rst b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/doc/introduction.rst
new file mode 100644
index 0000000..8ca6de6
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/doc/introduction.rst
@@ -0,0 +1,102 @@
+Introduction
+============
+
+Simpleline is a text UI framework. Originally a part of the Anaconda installer project.
+
+It is designed to be used with line-based machines and tools (e.g. serial console) so that
+every new line it appended to the bottom of the screen. Printed lines are never rewritten!
+
+How to use
+----------
+
+The best learning sources can be found in the
+`examples directory `_ in
+the `GitHub repository `_ and you can read the
+:ref:`guide_label` section of this documentation. However, some basic usage of Simpleline will be
+shown here too, to get an idea of how Simpleline works::
+
+ from simpleline import App
+ from simpleline.render.screen import UIScreen
+ from simpleline.render.screen_handler import ScreenHandler
+ from simpleline.render.widgets import TextWidget
+
+
+ # UIScreen is the main building item for Simpleline. Every screen
+ # which will user see should be inherited from UIScreen.
+ class HelloWorld(UIScreen):
+
+ def __init__(self):
+ # Set title of the screen.
+ super().__init__(title=u"Hello World")
+
+ def refresh(self, args=None):
+ # Fill the self.window attribute by the WindowContainer and set screen title as header.
+ super().refresh()
+ widget = TextWidget("Body text")
+ self.window.add_with_separator(widget)
+
+
+ if __name__ == "__main__":
+ # Initialize application (create scheduler and event loop).
+ App.initialize()
+
+ # Create our screen.
+ screen = HelloWorld()
+
+ # Schedule screen to the screen scheduler.
+ # This can be called only after App.initialize().
+ ScreenHandler.schedule_screen(screen)
+
+ # Run the application. You must have some screen scheduled
+ # otherwise it will end in an infinite loop.
+ App.run()
+
+The output from the simple *Hello World* example above::
+
+ $ ./run_example.sh 00_basic
+ ================================================================================
+ ================================================================================
+ Hello World
+
+ Body text
+
+ Please make a selection from the above ['c' to continue, 'q' to quit, 'r' to
+ refresh]:
+
+If a user presses **r** and then **enter** to refresh, the same screen is printed again.
+This will be printed to a monitor::
+
+ $ ./run_example.sh 00_basic
+ ================================================================================
+ ================================================================================
+ Hello World
+
+ Body text
+
+ Please make a selection from the above ['c' to continue, 'q' to quit, 'r' to
+ refresh]: r
+ ================================================================================
+ ================================================================================
+ Hello World
+
+ Body text
+
+ Please make a selection from the above ['c' to continue, 'q' to quit, 'r' to
+ refresh]:
+
+As you can see the whole screen is not rewritten -- only printed again on the bottom. This
+is the expected behavior so the actual screen is always at the bottom but you can see the whole
+history. This behavior makes working with line based machines and tools much easier.
+
+Dependencies
+------------
+
+This is a Python3-only project. This code should not be difficult to migrate to Python2. However,
+there is no need from the community, so it is only compatible with Python3 at the moment. No special
+libraries are required to use this library. If you want to use glib event loop instead of the
+original one you need to install glib and Python3 gobject introspection.
+
+If you want to run tests (make ci), you need to install
+`Pocketlint `_ and
+`glib `_ with gobject introspection for
+`Python3 `_.
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/doc/make.bat b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/doc/make.bat
new file mode 100644
index 0000000..63f06ba
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/doc/make.bat
@@ -0,0 +1,36 @@
+@ECHO OFF
+
+pushd %~dp0
+
+REM Command file for Sphinx documentation
+
+if "%SPHINXBUILD%" == "" (
+ set SPHINXBUILD=sphinx-build
+)
+set SOURCEDIR=.
+set BUILDDIR=_build
+set SPHINXPROJ=Simpleline
+
+if "%1" == "" goto help
+
+%SPHINXBUILD% >NUL 2>NUL
+if errorlevel 9009 (
+ echo.
+ echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
+ echo.installed, then set the SPHINXBUILD environment variable to point
+ echo.to the full path of the 'sphinx-build' executable. Alternatively you
+ echo.may add the Sphinx directory to PATH.
+ echo.
+ echo.If you don't have Sphinx installed, grab it from
+ echo.http://sphinx-doc.org/
+ exit /b 1
+)
+
+%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
+goto end
+
+:help
+%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
+
+:end
+popd
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/examples/00_basic/00_basic.py b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/examples/00_basic/00_basic.py
new file mode 100755
index 0000000..9ace13c
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/examples/00_basic/00_basic.py
@@ -0,0 +1,60 @@
+#!/bin/python3
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+
+# Basic usage of Simpleline.
+#
+# Only show screen with header and text in it.
+#
+
+from simpleline import App
+from simpleline.render.screen import UIScreen
+from simpleline.render.screen_handler import ScreenHandler
+from simpleline.render.widgets import TextWidget
+
+
+# UIScreen is the main building item for Simpleline. Every screen
+# which will user see should be inherited from UIScreen.
+class HelloWorld(UIScreen):
+
+ def __init__(self):
+ # Set title of the screen.
+ super().__init__(title=u"Hello World")
+
+ def refresh(self, args=None):
+ # Fill the self.window attribute by the WindowContainer and set screen title as header.
+ super().refresh()
+ widget = TextWidget("Body text")
+ self.window.add_with_separator(widget)
+
+
+if __name__ == "__main__":
+ # Initialize application (create scheduler and event loop).
+ App.initialize()
+
+ # Create our screen.
+ screen = HelloWorld()
+
+ # Schedule screen to the screen scheduler.
+ # This can be called only after App.initialize().
+ ScreenHandler.schedule_screen(screen)
+
+ # Run the application. You must have some screen scheduled
+ # otherwise it will end in an infinite loop.
+ App.run()
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/examples/01_base_widgets/01_base_widgets.py b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/examples/01_base_widgets/01_base_widgets.py
new file mode 100755
index 0000000..0f821f3
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/examples/01_base_widgets/01_base_widgets.py
@@ -0,0 +1,75 @@
+#!/bin/python3
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+
+# Usage of base widgets.
+#
+# Show base widgets for user interaction in one screen.
+#
+
+from simpleline import App
+from simpleline.render.screen import UIScreen
+from simpleline.render.screen_handler import ScreenHandler
+from simpleline.render.widgets import TextWidget, CenterWidget, CheckboxWidget
+
+
+class HelloWorld(UIScreen):
+
+ def __init__(self):
+ # Set title of the screen.
+ super().__init__(title=u"Show Widgets")
+
+ def refresh(self, args=None):
+ super().refresh()
+
+ # Text widget
+ # Show text to user. This is basic widget which will handle
+ # wrapping of words for you.
+ text_widget = TextWidget("Text widget")
+ self.window.add_with_separator(text_widget)
+
+ # Center widget
+ # Wrap extisting widget and center it to the middle of the screen.
+ text = TextWidget("Center widget")
+ center_widget = CenterWidget(text)
+ self.window.add_with_separator(center_widget, blank_lines=3) # Add two more blank lines
+
+ # Checkbox widget
+ # Checkbox which can hold 2 states.
+ checkbox_widget = CheckboxWidget(key="o",
+ title="Checkbox title",
+ text="Checkbox text",
+ completed=True)
+ self.window.add_with_separator(checkbox_widget)
+
+ # Checkbox widget unchecked
+ checkbox_widget_unchecked = CheckboxWidget(key="o",
+ title="Checkbox title",
+ text="Unchecked",
+ completed=False)
+ self.window.add_with_separator(checkbox_widget_unchecked)
+
+
+if __name__ == "__main__":
+ App.initialize()
+
+ screen = HelloWorld()
+ ScreenHandler.schedule_screen(screen)
+
+ App.run()
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/examples/02_ignore_continue/02_ignore_continue.py b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/examples/02_ignore_continue/02_ignore_continue.py
new file mode 100755
index 0000000..ba5e732
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/examples/02_ignore_continue/02_ignore_continue.py
@@ -0,0 +1,78 @@
+#!/bin/python3
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+
+# How to block the input from a user.
+#
+# This can be used for example to force a user to set all the required values.
+# Application quit callback is also used here.
+#
+
+from simpleline import App
+from simpleline.render.prompt import Prompt
+from simpleline.render.screen import UIScreen, InputState
+from simpleline.render.screen_handler import ScreenHandler
+from simpleline.render.widgets import TextWidget, CenterWidget
+
+
+def application_quit_cb(args):
+ """Call this callback when the application is quitting."""
+ print("Application is closing. Bye!")
+
+
+class InfiniteScreen(UIScreen):
+
+ def __init__(self):
+ # We are using title as message here. Any text could be passed to the title.
+ super().__init__("You need to use 'q' to quit")
+ self.continue_count = 0
+
+ def refresh(self, args=None):
+ """Print text to user with number of continue clicked."""
+ super().refresh(args)
+ # Print counter to the screen.
+ widget = TextWidget("You pressed {} times on continue".format(self.continue_count))
+ # Center this counter to middle of the screen.
+ center_widget = CenterWidget(widget)
+ # Add the centered widget to the window container.
+ self.window.add(center_widget)
+
+ def input(self, args, key):
+ """Catch 'c' keys for continue and increase counter."""
+ if key == Prompt.CONTINUE:
+ self.continue_count += 1
+ # Do not process 'c' continue anymore.
+ # This will refresh screen to refresh counter number
+ return InputState.PROCESSED_AND_REDRAW
+
+ # Process other input e.g.: 'r' refresh and 'q' quit.
+ return key
+
+
+if __name__ == "__main__":
+ App.initialize()
+ screen = InfiniteScreen()
+
+ # Get event loop from application.
+ loop = App.get_event_loop()
+ # Set quit callback to the loop. When the loop quits this callback will be triggered.
+ loop.set_quit_callback(application_quit_cb)
+
+ ScreenHandler.schedule_screen(screen)
+ App.run()
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/examples/03_entry_counter/03_entry_counter.py b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/examples/03_entry_counter/03_entry_counter.py
new file mode 100755
index 0000000..da26507
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/examples/03_entry_counter/03_entry_counter.py
@@ -0,0 +1,89 @@
+#!/bin/python3
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+
+from simpleline import App
+from simpleline.render.screen import UIScreen, InputState
+from simpleline.render.screen_handler import ScreenHandler
+from simpleline.render.widgets import TextWidget, CenterWidget
+
+
+class Hub(UIScreen):
+
+ def __init__(self):
+ super().__init__("Hub for entry counter")
+ self._counter_spoke = CounterScreen()
+
+ def refresh(self, args=None):
+ super().refresh(args)
+
+ w = CenterWidget(TextWidget("Press '1' to enter Entry counter"))
+
+ self.window.add_with_separator(w)
+
+ def input(self, args, key):
+ """Run spokes based on the user choice."""
+ if key == "1":
+ ScreenHandler.push_screen(self._counter_spoke)
+ # this input was processed
+ return InputState.PROCESSED
+
+ # return for outer processing
+ # the basic processing is 'c' for continue, 'r' for refresh, 'q' to quit
+ # otherwise the input is discarded and waiting for a new input
+ return key
+
+ def prompt(self, args=None):
+ """Add our information to the prompt."""
+ prompt = super().prompt(args)
+ prompt.add_option("1", "to enter counter spoke")
+ return prompt
+
+
+class CounterScreen(UIScreen):
+
+ def __init__(self):
+ super().__init__("Counter Screen")
+ self._counter = 0
+
+ def closed(self):
+ super().closed()
+ self.screen_ready = False
+
+ def setup(self, args=None):
+ super().setup(args)
+ self._counter += 1
+ return True
+
+ def refresh(self, args=None):
+ """Write message to user."""
+ super().refresh(args)
+ w = TextWidget("Counter {}".format(self._counter))
+ self.window.add_with_separator(CenterWidget(w))
+
+ @property
+ def counter(self):
+ return self._counter
+
+
+if __name__ == "__main__":
+ App.initialize()
+ hub = Hub()
+ ScreenHandler.schedule_screen(hub)
+ App.run()
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/examples/04_help/04_help.py b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/examples/04_help/04_help.py
new file mode 100755
index 0000000..cb73811
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/examples/04_help/04_help.py
@@ -0,0 +1,37 @@
+#!/bin/python3
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+
+# Show help screen.
+#
+# Usage of the HelpScreen advanced widget.
+# There are many of advanced widgets which have different uses.
+# I've recommend developer to look on them.
+
+from simpleline import App
+from simpleline.render.screen_handler import ScreenHandler
+from simpleline.render.adv_widgets import HelpScreen
+
+
+if __name__ == "__main__":
+ App.initialize()
+ # You need to pass file with help text to the screen.
+ s = HelpScreen("./04_help/example_help.txt")
+ ScreenHandler.schedule_screen(s)
+ App.run()
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/examples/04_help/example_help.txt b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/examples/04_help/example_help.txt
new file mode 100644
index 0000000..c5a26fe
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/examples/04_help/example_help.txt
@@ -0,0 +1,3 @@
+This is help to our AWESOME application.
+
+Everything is so simple, therefore no help is needed!
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/examples/05_hub_spoke/05_hub_spoke.py b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/examples/05_hub_spoke/05_hub_spoke.py
new file mode 100755
index 0000000..498a177
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/examples/05_hub_spoke/05_hub_spoke.py
@@ -0,0 +1,202 @@
+#!/bin/python3
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+# Hub and spoke implementation.
+#
+
+# Advanced example of Simpleline use.
+# Hub is the main screen from where you can go to spokes and do work in the spokes
+# then you will return to the hub back. You can continue when all the required items
+# is set.
+# The example of containers use will be showed here.
+#
+
+from simpleline import App
+from simpleline.render.adv_widgets import PasswordDialog
+from simpleline.render.containers import ListRowContainer
+from simpleline.render.prompt import Prompt
+from simpleline.render.screen import UIScreen, InputState
+from simpleline.render.screen_handler import ScreenHandler
+from simpleline.render.widgets import TextWidget, CenterWidget
+
+
+class Hub(UIScreen):
+
+ def __init__(self):
+ super().__init__("Hub")
+
+ # Container will be used for spokes positioning. Container is always created in
+ # the refresh() method.
+ self._container = None
+
+ self._create_spokes()
+
+ def _create_spokes(self):
+ """Create spokes and use their value."""
+
+ # Create name spoke
+ self._name_spoke = SetNameScreen("First name", "John")
+ # Create surname spoke
+ self._surname_spoke = SetNameScreen("Surname", "Doe")
+ # Create the PasswordDialog advanced widget for getting password from a user.
+ self._password_spoke = PasswordDialog()
+
+ def refresh(self, args=None):
+ """Refresh method is called always before the screen will be printed.
+
+ All items for printing should be updated or created here.
+ """
+ # Init window container. The windows container will be erased here.
+ # The window container is the base container. Everything for rendering should be put
+ # into this container, including other containers.
+ super().refresh(args)
+
+ # Add the screen header message before our items.
+ header = TextWidget("Please complete all the spokes to continue")
+ header = CenterWidget(header)
+ self.window.add_with_separator(header, blank_lines=2)
+
+ # Create the empty container.
+ # It will add numbering, process user input and positioning for us.
+ self._container = ListRowContainer(2)
+
+ # Create widget to get user name.
+ widget = self._create_name_widget()
+ # Add widget, callback, arguments to the container.
+ #
+ # widget - Widget we want to render. It will be numbered automatically.
+ # Could be container if needed.
+ # callback - This callback will be called by the ListRowContainer.process_user_input()
+ # method when a user press the number of this item. Callback will get args
+ # passed as 3rd argument.
+ # args - Argument for callback.
+ self._container.add(widget, self._push_screen_callback, self._name_spoke)
+
+ # Create surname widget and add it to the container.
+ widget = self._create_surname_widget()
+ self._container.add(widget, self._push_screen_callback, self._surname_spoke)
+
+ # Create password widget and add it to the container.
+ widget = self._create_password_widget()
+ self._container.add(widget, self._push_screen_callback, self._password_spoke)
+
+ # Add the ListRowContainer container to the WindowContainer container.
+ self.window.add_with_separator(self._container)
+
+ def _create_name_widget(self):
+ """Create name spoke widget.
+
+ Add the actual value below the spoke name.
+ """
+ msg = "First name"
+ if self._name_spoke.value:
+ msg += "\n{}".format(self._name_spoke.value)
+
+ return TextWidget(msg)
+
+ def _create_surname_widget(self):
+ """Create surname spoke widget.
+
+ Add the actual value below the spoke name.
+ """
+ msg = "Surname"
+ if self._surname_spoke.value:
+ msg += "\n{}".format(self._surname_spoke.value)
+
+ return TextWidget(msg)
+
+ def _create_password_widget(self):
+ """Create password spoke widget.
+
+ Add the "Password set" text below the spoke name if set.
+ """
+ msg = "Password"
+ if self._password_spoke.answer:
+ msg += "\nPassword set."
+
+ return TextWidget(msg)
+
+ def input(self, args, key):
+ """Run spokes based on the user choice."""
+ # Find out if a user pressed number for an existing widget and call the callback attached
+ # to it with arguments passed in the refresh() method.
+ # Return False if the input is not related to the widget.
+ if self._container.process_user_input(key):
+ # Do not process other input if spoke is entered.
+ return InputState.PROCESSED
+
+ # Block continue ('c') if everything is not set.
+ if key == Prompt.CONTINUE:
+ if self._name_spoke and self._surname_spoke and self._password_spoke.answer:
+ return key
+ # catch 'c' key if not everything set
+ return InputState.DISCARDED
+
+ return key
+
+ @staticmethod
+ def _push_screen_callback(target_screen):
+ """Push target screen as new screen.
+
+ Target screen is passed in as an argument in the refresh() method.
+ """
+ ScreenHandler.push_screen(target_screen)
+
+ def prompt(self, args=None):
+ """Add information to prompt for user."""
+ prompt = super().prompt(args)
+ # Give user hint that he can press 1, 2 or 3 to enter spokes.
+ prompt.add_option("1,2,3", "to enter spokes")
+ return prompt
+
+
+class SetNameScreen(UIScreen):
+
+ def __init__(self, message, def_value):
+ """Create spoke for setting name and surname.
+
+ :param message: Text message as the body for user.
+ :param def_value: Default value for this spoke.
+ """
+ super().__init__()
+ self._value = def_value
+ self._message = message
+
+ def refresh(self, args=None):
+ """Write message to user."""
+ super().refresh(args)
+ w = TextWidget(self._message)
+ self.window.add(CenterWidget(w))
+
+ def prompt(self, args=None):
+ """Take user input."""
+ self._value = self.get_user_input("Write your name: ")
+ self.close()
+
+ @property
+ def value(self):
+ """Return value set by a user in this spoke."""
+ return self._value
+
+
+if __name__ == "__main__":
+ App.initialize()
+ screen = Hub()
+ ScreenHandler.schedule_screen(screen)
+ App.run()
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/examples/06_glib_loop/06_glib_loop.py b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/examples/06_glib_loop/06_glib_loop.py
new file mode 100755
index 0000000..94147c6
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/examples/06_glib_loop/06_glib_loop.py
@@ -0,0 +1,62 @@
+#!/bin/python3
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+
+# Use glib event loop instead of the original Simpleline loop.
+#
+# You need to have the python3-gobject installed.
+#
+# You can install it on Fedora by running:
+#
+# dnf install python3-gobject-base
+#
+#
+# This is basic example using Glib event loop.
+# You can implement your own loop abstraction. See simpleline/event_loop/glib_event_loop.
+#
+
+from simpleline import App
+from simpleline.event_loop.glib_event_loop import GLibEventLoop
+from simpleline.render.screen import UIScreen
+from simpleline.render.screen_handler import ScreenHandler
+from simpleline.render.widgets import TextWidget
+
+
+class HelloWorld(UIScreen):
+
+ def __init__(self):
+ super().__init__(title=u"Hello World with GLib")
+
+ def refresh(self, args=None):
+ super().refresh()
+ self.window.add_with_separator(TextWidget("Body text"))
+
+
+if __name__ == "__main__":
+ # Create Glib event loop.
+ glib_loop = GLibEventLoop()
+ # Use glib event loop instead of the original one.
+ # Everything else should behave the same as with the original Simpleline loop.
+ App.initialize(event_loop=glib_loop)
+
+ screen = HelloWorld()
+ ScreenHandler.schedule_screen(screen)
+
+ # Run the application.
+ App.run()
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/examples/07_divider/07_divider.py b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/examples/07_divider/07_divider.py
new file mode 100755
index 0000000..6c74f55
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/examples/07_divider/07_divider.py
@@ -0,0 +1,102 @@
+#!/bin/python3
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+
+# Simple divider screen.
+#
+# User input processing example.
+#
+#
+
+import re
+
+from simpleline import App
+from simpleline.render.screen import UIScreen, InputState
+from simpleline.render.screen_handler import ScreenHandler
+from simpleline.render.widgets import TextWidget
+
+
+class DividerScreen(UIScreen):
+
+ def __init__(self):
+ # Set title of the screen.
+ super().__init__(title=u"Divider")
+ self._message = 0
+
+ def refresh(self, args=None):
+ # Fill the self.window attribute by the WindowContainer and set screen title as header.
+ super().refresh()
+
+ widget = TextWidget("Result: " + str(self._message))
+ self.window.add_with_separator(widget)
+
+ def prompt(self, args=None):
+ # Change user prompt
+ prompt = super().prompt()
+
+ # Set message to the user prompt. Give a user hint how he/she may control our application.
+ prompt.set_message("Pass numbers to divider in a format: 'num / num'")
+
+ # Remove continue option from the control. There is no need for that
+ # when we have only one screen.
+ prompt.remove_option('c')
+
+ return prompt
+
+ def input(self, args, key):
+ """Process input from user and catch numbers with '/' symbol."""
+
+ # Test if user passed valid input for divider.
+ # This will basically take number + number and nothing else and only positive numbers.
+ groups = re.match(r'(\d+) *\/ *(\d+)$', key)
+ if groups:
+ num1 = int(groups[1])
+ num2 = int(groups[2])
+
+ # Dividing by zero is not valid so we won't accept this input from the user. New
+ # input is then required from the user.
+ if num2 == 0:
+ return InputState.DISCARDED
+
+ self._message = int(num1 / num2)
+
+ # Because this input is processed we need to show this screen (show the result).
+ # This will call refresh so our new result will be processed inside of the refresh()
+ # method.
+ return InputState.PROCESSED_AND_REDRAW
+
+ # Not input for our screen, try other default inputs. This will result in the
+ # same state as DISCARDED when no default option is used.
+ return key
+
+
+if __name__ == "__main__":
+ # Initialize application (create scheduler and event loop).
+ App.initialize()
+
+ # Create our screen.
+ screen = DividerScreen()
+
+ # Schedule screen to the screen scheduler.
+ # This can be called only after App.initialize().
+ ScreenHandler.schedule_screen(screen)
+
+ # Run the application. You must have some screen scheduled
+ # otherwise it will end in an infinite loop.
+ App.run()
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/examples/run_example.sh b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/examples/run_example.sh
new file mode 100755
index 0000000..4916480
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/examples/run_example.sh
@@ -0,0 +1,51 @@
+#!/bin/bash
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+
+# Script for starting examples from source code.
+#
+# Author(s): Jiri Konecny
+#
+
+
+function print_help {
+ echo "run_example.sh - easy way how to run example without installing module"
+ echo ""
+ echo "./run_example.sh [example]"
+ echo ""
+ echo "There is one required argument [example] which is name of the test."
+ echo ""
+}
+
+if [[ $# -ne 1 ]]; then
+ echo "Bad number of arguments" 1>&2
+ print_help
+ exit 1
+elif [[ "$1" == "-h" ]] || [[ "$1" == "--help" ]]; then
+ print_help
+ exit 0
+fi
+
+PROJECT_NAME=${1%%/}
+
+pushd $(pwd)
+cd $(dirname $0)/
+PYTHONPATH="..:." python3 ./$PROJECT_NAME/$PROJECT_NAME.py
+popd
+
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/po/Makefile b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/po/Makefile
new file mode 100644
index 0000000..772e0a2
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/po/Makefile
@@ -0,0 +1,86 @@
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+
+# Makefile for the PO files (translation) catalog
+#
+# $Id$
+
+TOP = ../..
+
+# What is this package?
+NLSPACKAGE = python-simpleline
+POTFILE = $(NLSPACKAGE).pot
+INSTALL = /usr/bin/install -c
+INSTALL_DATA = $(INSTALL) -m 644
+INSTALL_DIR = /usr/bin/install -d
+
+# destination directory
+INSTALL_NLS_DIR = $(RPM_BUILD_ROOT)/usr/share/locale
+
+# PO catalog handling
+MSGMERGE = msgmerge -v
+XGETTEXT = xgettext --default-domain=$(NLSPACKAGE) \
+ --add-comments
+MSGFMT = msgfmt --statistics --verbose
+
+# What do we need to do
+POFILES = $(wildcard *.po)
+MOFILES = $(patsubst %.po,%.mo,$(POFILES))
+PYSRC = $(wildcard ../simpleline/*.py) $(wildcard ../simpleline/**/*.py)
+
+all:: update-po $(MOFILES)
+
+potfile: $(PYSRC)
+ $(XGETTEXT) -L Python --keyword=_ --keyword=N_ $(PYSRC)
+ @if cmp -s $(NLSPACKAGE).po $(POTFILE); then \
+ rm -f $(NLSPACKAGE).po; \
+ else \
+ mv -f $(NLSPACKAGE).po $(POTFILE); \
+ fi; \
+
+update-po: Makefile potfile refresh-po
+
+refresh-po: Makefile
+ for cat in $(POFILES); do \
+ lang=`basename $$cat .po`; \
+ if $(MSGMERGE) $$lang.po $(POTFILE) > $$lang.pot ; then \
+ mv -f $$lang.pot $$lang.po ; \
+ echo "$(MSGMERGE) of $$lang succeeded" ; \
+ else \
+ echo "$(MSGMERGE) of $$lang failed" ; \
+ rm -f $$lang.pot ; \
+ fi \
+ done
+
+clean:
+ @rm -fv *mo *~ .depend
+
+install: $(MOFILES)
+ @for n in $(MOFILES); do \
+ l=`basename $$n .mo`; \
+ $(INSTALL_DIR) $(INSTALL_NLS_DIR)/$$l/LC_MESSAGES; \
+ $(INSTALL_DATA) --verbose $$n $(INSTALL_NLS_DIR)/$$l/LC_MESSAGES/$(NLSPACKAGE).mo; \
+ done
+
+%.mo: %.po
+ $(MSGFMT) -o $@ $<
+
+.PHONY: missing depend
+
+
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/python-simpleline.spec b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/python-simpleline.spec
new file mode 100644
index 0000000..10d38ed
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/python-simpleline.spec
@@ -0,0 +1,408 @@
+%global srcname simpleline
+
+Name: python-%{srcname}
+Summary: A Python library for creating text UI
+Url: https://github.com/rhinstaller/python-%{srcname}
+Version: 1.8
+Release: 1%{?dist}
+# This tarball was created from upstream git:
+# git clone https://github.com/rhinstaller/python-simpleline
+# cd python-simpleline && make archive
+Source0: https://github.com/rhinstaller/python-%{srcname}/releases/download/%{srcname}-%{version}/%{srcname}-%{version}.tar.gz
+
+License: LGPLv3+
+BuildArch: noarch
+BuildRequires: make
+BuildRequires: python3-devel
+BuildRequires: gettext
+BuildRequires: python3-setuptools
+BuildRequires: intltool
+BuildRequires: python3-gobject-base
+
+%description
+Simpleline is a Python library for creating text UI.
+It is designed to be used with line-based machines
+and tools (e.g. serial console) so that every new line
+is appended to the bottom of the screen.
+Printed lines are never rewritten!
+
+
+%package -n python3-%{srcname}
+Summary: A Python3 library for creating text UI
+Requires: rpm-python3
+
+%{?python_provide:%python_provide python3-%{srcname}}
+
+%description -n python3-%{srcname}
+Simpleline is a Python3 library for creating text UI.
+It is designed to be used with line-based machines
+and tools (e.g. serial console) so that every new line
+is appended to the bottom of the screen.
+Printed lines are never rewritten!
+
+%prep
+%setup -q -n %{srcname}-%{version}
+
+%build
+%make_build
+
+%install
+make DESTDIR=%{buildroot} install
+%find_lang python-%{srcname}
+
+%check
+make test
+
+
+%files -n python3-%{srcname} -f python-%{srcname}.lang
+%license LICENSE.md
+%doc ChangeLog README.md
+%{python3_sitelib}/*
+
+%changelog
+* Mon Feb 22 2021 Jiri Konecny - 1.8-1
+- Add missing make command to spec file (jkonecny)
+- Don't translate the prompt keys (vponcova)
+- Enable daily build for Anaconda-devel COPR repository (jkonecny)
+- Use Fedora container registry instead of Dockerhub (jkonecny)
+- Migrate COPR daily COPR builds to Packit (jkonecny)
+- Test build on Fedora ELN (jkonecny)
+- Remove packit get-current-action (jkonecny)
+- Run tests in GitHub workflow (martin)
+- Fix raise-missing-from (W0707) pylint warnings (martin)
+- Fix pylint to check test code correctly (jkonecny)
+- Use script to run unit tests (jkonecny)
+- Use relative imports in tests (jkonecny)
+- Change directory structure of unit tests (jkonecny)
+- Fix documentation of _process_screen method (jkonecny)
+- Fix pylint issues (jkonecny)
+- Use pylint instead of pocketlint (jkonecny)
+- Add coverage support (jkonecny)
+- Make link to exmples directory in Readme (jkonecny)
+- Fix homepage of the project in setup.py (jkonecny)
+- Fix classifiers in setup.py (jkonecny)
+- Add pypi-upload to Makefile (jkonecny)
+- Use correct variant of the field (jkonecny)
+- Propose Fedora update only to Fedora in development (jkonecny)
+- Add upstream tag template to packit for releasing (jkonecny)
+- Packit will download archive from Source0 if needed (jkonecny)
+
+* Thu Jun 25 2020 Jiri Konecny - 1.7-1
+- Testing farm seems like a better option (jkonecny)
+- Fix upstream package name (jkonecny)
+- Copy archive to the project root for packit (jkonecny)
+- Use BUILD_ARGS for packit to remove wheel dependency (jkonecny)
+- Correct how packit sniffing package version (jkonecny)
+- Add packit action to create archive correctly (jkonecny)
+- Simplify Packit configuration file (jkonecny)
+- Enable copr builds and add packit config (dhodovsk)
+- Add possibility to change setup.py arguments (jkonecny)
+- Fix typo in setup.py (jkonecny)
+- Add translation badge from Weblate (jkonecny)
+- Add potfile to the .gitignore (jkonecny)
+- Pause releasing when calling bumpver (jkonecny)
+- Remove unused PREFIX from the Makefile (jkonecny)
+- Remove Zanata from the Simpleline (jkonecny)
+- Switch po-push from Zanata to Weblate (jkonecny)
+- Download translations from Weblate (jkonecny)
+- Remove pot file from this repository (jkonecny)
+- Add new directories to .gitignore and make clean (jkonecny)
+- Add Makefile PHONY targets (jkonecny)
+- Simplify archive creation (jkonecny)
+- Reflect rename of COPYING to LICENSE.md (jkonecny)
+- Change license in spec file to LGPLv3+ (jkonecny)
+- Add COPR badge for daily builds (jkonecny)
+- Improve setup.py (jkonecny)
+- Change license to LGPLv3 for all the files (jkonecny)
+- Change License from GPLv2+ to LGPLv3+ (jkonecny)
+
+* Thu Aug 22 2019 Jiri Konecny - 1.6-1
+- Always close the password dialog (vponcova)
+- Remove unnecessary pass statements (jkonecny)
+
+* Mon May 13 2019 Jiri Konecny - 1.5-1
+- Update spec file from the downstream (jkonecny)
+- Fix translation issue for lt language (jkonecny)
+
+* Sat Feb 02 2019 Fedora Release Engineering - 1.4-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild
+
+* Mon Nov 19 2018 Jiri Konecny - 1.4-1
+- Fix and add tests for the new changes (#1646568) (jkonecny)
+- Add should_run_with_empty_stack configuration (#1646568) (jkonecny)
+- Remove wrong line in password_function conf (jkonecny)
+- Tweak date lang settings in make bumpver command (jkonecny)
+- Drop python-pocketlint build dependency (jkonecny)
+- Update spec file from Fedora (jkonecny)
+
+* Sat Jul 14 2018 Fedora Release Engineering - 1.3-5
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild
+
+* Mon Jul 02 2018 Miro Hrončok - 1.3-4
+- Rebuilt for Python 3.7
+
+* Wed Jun 20 2018 Jiri Konecny - 1.3-3
+- Drop python-pocketlint dependency
+
+* Mon Jun 18 2018 Miro Hrončok - 1.3-2
+- Rebuilt for Python 3.7
+
+* Mon Jun 11 2018 Jiri Konecny - 1.3-1
+- Fix pylint errors raised by new pylint version (jkonecny)
+- Setup logging handler properly for a library (jkonecny)
+
+* Tue May 29 2018 Jiri Konecny - 1.2-1
+- Document GlobalConfiguration class (jkonecny)
+- Add advanced input processing section to docs (jkonecny)
+- Make makefile zanata check more robust (jkonecny)
+- Add missing newline characters at the end (jkonecny)
+- Abstract widget buffer extensions (jkonecny)
+- Use python 3 sphinx module (jkonecny)
+
+* Tue Apr 10 2018 Jiri Konecny - 1.1-1
+- Add global configuration initialize test (jkonecny)
+- Use default password function from GlobalConfiguration (jkonecny)
+- Add tests for GlobalConfiguration class (jkonecny)
+- Move default width to GlobalConfiguration class (jkonecny)
+- Add GlobalConfiguration object (jkonecny)
+- Fix a missing article in docs. (jkonecny)
+- Add tests for UIScreen get_user_input method (jkonecny)
+- Only InputManager is used to get input in UIScreen (jkonecny)
+- Move emit of InputReadySignal to InputRequest (jkonecny)
+- Get only last input on concurrent input (jkonecny)
+- Add concurrency tests for InputHandler (jkonecny)
+- Add requester source to the InputHandler (#1557472) (jkonecny)
+- Use InputManager as screen helper not in ScreenScheduler (#1557472) (jkonecny)
+- Add test to check InputThreadManager after initialization (jkonecny)
+- Reinitialize InputThreadManager when app is initialized (jkonecny)
+- Input requests are now handled by InputThreadManager (jkonecny)
+- Remove io_manager property from ScreenScheduler (jkonecny)
+- Rename io_manager to input_manager (jkonecny)
+- Change IOManager to InputManager (jkonecny)
+- Move draw from IOManager to ScreenScheduler (jkonecny)
+- Separate password input to PasswordInputHandler (jkonecny)
+- Add DEFAULT_WIDTH constant (jkonecny)
+- Add tests for InputHandler (jkonecny)
+- Replace get_input_without_check by new property (jkonecny)
+- Separate input to a new module (jkonecny)
+
+* Wed Feb 28 2018 Jiri Konecny - 1.0-1
+- Fix docs based on the new PROCESSED feature (jkonecny)
+- Use new PROCESS_AND* in examples (jkonecny)
+- Add tests for PROCESSED_AND_{CLOSE|REDRAW} (jkonecny)
+- Use new PROCESSED_AND_{CLOSE|REDRAW} in advanced widgets (jkonecny)
+- Support PROCESSED_AND_CLOSE UserInputState (jkonecny)
+- Rename UserInputResult to UserInputAction (jkonecny)
+- Support PROCESSED_AND_REDRAW (jkonecny)
+- Fix link to the documentation in README (jkonecny)
+- Add link to the documentation (jkonecny)
+- Write documentation for Simpleline (jkonecny)
+- Disable pylint error for doc copyright variable (jkonecny)
+- Add new example used in the documentation (jkonecny)
+- Mock modules for readthedocs (jkonecny)
+- Do a small fixes to improve documentation (jkonecny)
+- Get version from the spec file (jkonecny)
+- Add documentation skeleton (jkonecny)
+
+* Thu Oct 19 2017 Jiri Konecny - 0.8-1
+- Test clean-up (jkonecny)
+- Move and rename force_quit loop (jkonecny)
+- Add dump screen stack method (jkonecny)
+
+* Tue Oct 17 2017 Jiri Konecny - 0.7-1
+- Fix crash when container callback is not set (jkonecny)
+- Add EntryWidget to show item title and value (jkonecny)
+- Add GetPasswordInputScreen for getting passwords (jkonecny)
+- UIScreen can have hidden input (jkonecny)
+- Move getpass func inside locks (jkonecny)
+- Implement GetInputScreen (jkonecny)
+- Do not print new line with empty container (jkonecny)
+- Add no_separator to UIScreen (jkonecny)
+- Fix HelpScreen title (jkonecny)
+- Remove original classes from GLib tests (jkonecny)
+- Fix test case name (jkonecny)
+- Move tests to subfolder (jkonecny)
+- Move base widgets as second example (jkonecny)
+- Add example with basic widgets usage (jkonecny)
+- Improve README.md (jkonecny)
+- Improve example starting script (jkonecny)
+- Add glib loop example (jkonecny)
+- Add prefixes to examples to impress difficulty (jkonecny)
+- Add comments to the examples (jkonecny)
+- Update examples to use new features (jkonecny)
+
+* Fri Sep 08 2017 Jiri Konecny - 0.6-1
+- Implement the force quit all loops feature (jkonecny)
+- Improved GLib event loop testing (jkonecny)
+- Handle GLib event loop exceptions better (jkonecny)
+- Support for GLib event loop (jkonecny)
+- Wrap exceptions from handlers as ExceptionSignal (jkonecny)
+- Only highest priority events are processed in one iteration (jkonecny)
+- Move parts from MainLoop to AbstractEventLoop (jkonecny)
+- MainLoop won't wait in busy loop anymore (jkonecny)
+- Don't block loop when waiting on user input (jkonecny)
+- Fix user input lock (jkonecny)
+
+* Mon Sep 04 2017 Jiri Konecny - 0.5-1
+- Fix exception in Screen input caused infinite loop (jkonecny)
+- Add test if app is initialized (jkonecny)
+- Redraw on first scheduled screen (#1487326) (jkonecny)
+- Redraw after YesNoDialog modal window close (jkonecny)
+
+* Fri Aug 18 2017 Jiri Konecny - 0.4-1
+- Screen scheduling is moved to ScreenHandler (jkonecny)
+- Remove merge commits from changelog (jkonecny)
+
+* Thu Aug 17 2017 Jiri Konecny - 0.3-1
+- Remove merge commits from changelog (jkonecny)
+- Fix bad input processing for list containers (jkonecny)
+- Rename quit callback and add args there. (jkonecny)
+- Add tests for new container feature (jkonecny)
+- List containers take max width if not specified (jkonecny)
+- Remove leftover print in containers parsing input (jkonecny)
+
+* Mon Aug 14 2017 Jiri Konecny - 0.2-1
+- Merge pull request #26 from jkonecny12/master-fix-ExitMainLoop-exception (jkonecny)
+- Merge pull request #28 from jkonecny12/master-fix-container-callback (jkonecny)
+- Fix docs for container callbacks (jkonecny)
+- Merge pull request #25 from jkonecny12/master-fix-big-screen-printing (jkonecny)
+- Remove missed test helper for local testing (jkonecny)
+- The ExitMainLoop exception should kill whole app (jkonecny)
+- Do not run an old event queue from modal screen (jkonecny)
+- Merge pull request #23 from jkonecny12/master-add-possibility-to-end-loop-politely (jkonecny)
+- Merge pull request #24 from jkonecny12/master-fix-compatibility (jkonecny)
+- Add possibility to end loop politely (jkonecny)
+- Merge pull request #22 from jkonecny12/master-add-logging (jkonecny)
+- Fix printing issues for bigger screens (jkonecny)
+- Set default screen height to 30 (jkonecny)
+- Keep backward compatibility for UIScreen args (jkonecny)
+- Add logging to the Simpleline (jkonecny)
+- Merge pull request #19 from jkonecny12/master-rework-wait-on-input (jkonecny)
+- Merge pull request #21 from jkonecny12/master-fix-adv-widgets (jkonecny)
+- Merge pull request #20 from jkonecny12/master-move-InputState (jkonecny)
+- Wait on input thread to finish (jkonecny)
+- return_after don't skip signals in recursion (jkonecny)
+- Add TicketMachine helper class (jkonecny)
+- Return user input in the signal handler (jkonecny)
+- Merge pull request #16 from jkonecny12/master-modify-exception-handling (jkonecny)
+- Merge pull request #18 from jkonecny12/master-ignore-pylint-with-lock (jkonecny)
+- Reflect changes in adv_widgets (jkonecny)
+- Move InputState next to UIScreen (jkonecny)
+- Break App's cyclic imports (jkonecny)
+- Add temporal pylint-disable for with Lock (jkonecny)
+- Re-raise exception only if no handler is registered (jkonecny)
+- Merge pull request #15 from jkonecny12/master-thread-safe-eventqueue (jkonecny)
+- Merge pull request #11 from jkonecny12/master-add-args-to-scheduler-shortcut (jkonecny)
+- Make the EventQueue thread safe (jkonecny)
+- Merge pull request #13 from jkonecny12/master-enhance-WindowContainer-add (jkonecny)
+- Merge pull request #12 from jkonecny12/master-fix-ready-collision (jkonecny)
+- Add args param to the scheduler_handler shortcuts (jkonecny)
+- Fix bad param name for scheduling methods (jkonecny)
+- Merge pull request #10 from jkonecny12/master-add-hidden-user-input (jkonecny)
+- Merge pull request #8 from jkonecny12/master-remove-base-file (jkonecny)
+- Merge pull request #9 from jkonecny12/master-remove-quit-message (jkonecny)
+- Add method add_with_separator to WindowContainer (jkonecny)
+- Fix `ready` property collision with Anaconda (jkonecny)
+- Add hidden parameter to UIScreen get input (jkonecny)
+- Remove unused quit message (jkonecny)
+- Move App from base to __init__.py (jkonecny)
+- Merge pull request #6 from jkonecny12/fix-make-potfile-generation (jkonecny)
+- Merge pull request #7 from jkonecny12/master-fix-make-archive (jkonecny)
+- Fix make archive (jkonecny)
+- Fix potfile generation (jkonecny)
+- Merge pull request #5 from jkonecny12/master-add-containers (jkonecny)
+- Examples now using WindowContainer (jkonecny)
+- Fix error when getting input directly (jkonecny)
+- Add WindowContainer and use it in UIScreen (jkonecny)
+- Add SeparatorWidget (jkonecny)
+- Add input processing to containers (jkonecny)
+- Add containers numbering (jkonecny)
+- Add containers, checkbox and center widget tests (jkonecny)
+- Add row and column list containers (jkonecny)
+- Merge pull request #4 from jkonecny12/master-split-renderer (jkonecny)
+- Fix setup.py modules (jkonecny)
+- Change INPUT_PROCESSED and INPUT_DISCARDED to Enum (jkonecny)
+- Rename renderer and event_loop in App class (jkonecny)
+- Add scheduler_handler and rename switch_screen (jkonecny)
+- Process signals when waiting on user input (jkonecny)
+- Move draw screen to the InOutManager (jkonecny)
+- Change UIScreen properties (jkonecny)
+- Move input processing to the InOutManager class (jkonecny)
+- Move SignalHandler to the screen module (jkonecny)
+- Move ui_screen to the screen module (jkonecny)
+- Rename Renderer to ScreenScheduler (jkonecny)
+- Merge pull request #3 from jkonecny12/master-change-close-to-signal (jkonecny)
+- Change separator tests to reflect close as signal (jkonecny)
+- Close screen is now signal (jkonecny)
+- Rename emit_draw_signal to redraw (jkonecny)
+- Merge pull request #2 from jkonecny12/master-fix-modal (jkonecny)
+- Add missing doc for EventQueue and ScreeStack (jkonecny)
+- Add tests for event queue (jkonecny)
+- Modal screens are now executing new event loop (jkonecny)
+- Separator should be printed just before show_all() (jkonecny)
+- Move screen scheduling tests to new file (jkonecny)
+- Merge pull request #1 from jkonecny12/master-refactorization (jkonecny)
+- Implement small fixes and tweaks (jkonecny)
+- Move ScreenStack to render module (jkonecny)
+- Add SimplelineError as base class for Exceptions (jkonecny)
+- Clean up unused code and do a small tweaks (jkonecny)
+- Fix pylint errors (jkonecny)
+- Modify examples to reflect new changes (jkonecny)
+- Add emit_draw_signal to the SignalHandler (jkonecny)
+- Do not call draw when prompt is not present (jkonecny)
+- Remove old INPUT_* constants (jkonecny)
+- Do not redraw when input was processed (jkonecny)
+- Add license to all test classes (jkonecny)
+- Add new tests for render_screen, renderer, widgets (jkonecny)
+- Remove hubQ (jkonecny)
+- Split input from drawing (jkonecny)
+- Fix rendering errors by removing _redraw variable (jkonecny)
+- Replace array with priority by Signal ordering (jkonecny)
+- Add tests for SignalHandler (jkonecny)
+- Use SignalHandler (jkonecny)
+- Add screen rendering tests (jkonecny)
+- Raise exceptions instead of silently quitting (jkonecny)
+- Reflect code changes in tests (jkonecny)
+- Fix missing super().__init__() in Signals (jkonecny)
+- Fix modal screen by removing execute_new_loop (jkonecny)
+- Fail when UIScreen is not closing itself (jkonecny)
+- Add constants INPUT_PROCESSED and INPUT_DISCARDED (jkonecny)
+- Fix issues in Renderer and MainLoop (jkonecny)
+- Use RenderScreenSignal signal for rendering (jkonecny)
+- Add App.run as a shortcut to start application (jkonecny)
+- Move widgets and adv_widgets to render module (jkonecny)
+- Fix cosmetic bugs in widgets (jkonecny)
+- Move prompt to render module (jkonecny)
+- Modify ScreenStack tests to reflect UIScreen changes (jkonecny)
+- Modify UIScreen to use new App class structure (jkonecny)
+- Fix renderer tests after moving the Renderer class (jkonecny)
+- Move renderer to separate file renderer.py (jkonecny)
+- Add quit_callback to the event loop abstract class (jkonecny)
+- Add tests for the App class (jkonecny)
+- Change base class to singleton like class (jkonecny)
+- Add tests for event loop processing (jkonecny)
+- Implementation of defualt event loop and signals (jkonecny)
+- Add abstract base classes for EventLoop and Event (jkonecny)
+- Remove ScheduleScreen tests (jkonecny)
+- Add tests for renderer (jkonecny)
+- Extract renderer from App class to render directory (jkonecny)
+- Add tests for new ScreenStack and ScreenData (jkonecny)
+- Extract screen stack to its own object and data class (jkonecny)
+- Modify comments in the spec file (jkonecny)
+- Package is named python-simpleline (jkonecny)
+- Fix spec file (jkonecny)
+- Fix summary in the spec file (jkonecny)
+- Fix names according to tagging (jkonecny)
+
+* Fri May 5 2017 Jiri Konecny - 0.1-3
+- Modify comments in the spec file
+
+* Thu May 4 2017 Jiri Konecny - 0.1-2
+- Drop clean section
+- Drop Group, it is not needed
+- Use make_build macro
+- Reorder check and install sections
+- Rename package to python-simpleline but rpm will be python3-simpleline
+
+* Fri Dec 16 2016 Jiri Konecny - 0.1-1
+- Initial package
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/setup.py b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/setup.py
new file mode 100644
index 0000000..9534796
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/setup.py
@@ -0,0 +1,43 @@
+#!/usr/bin/python3
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+
+from setuptools import setup, find_packages
+
+with open("README.md", "r") as f:
+ long_description = f.read()
+
+setup(name='simpleline',
+ version='1.8',
+ author='Jiri Konecny',
+ author_email='jkonecny@redhat.com',
+ description='Python text UI framework',
+ long_description=long_description,
+ long_description_content_type="text/markdown",
+ keywords='ui text library glib',
+ url='https://github.com/rhinstaller/python-simpleline',
+ packages=find_packages(include=['simpleline', 'simpleline.*']),
+ classifiers=[
+ "Development Status :: 5 - Production/Stable",
+ "Intended Audience :: Developers",
+ "Topic :: Software Development :: User Interfaces",
+ "License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+)",
+ "Programming Language :: Python :: 3",
+ ],
+ python_requires='>=3.4')
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/__init__.py b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/__init__.py
new file mode 100644
index 0000000..1d4dfc7
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/__init__.py
@@ -0,0 +1,136 @@
+# Base class for Simpleline Text UI framework.
+#
+# Library containing the Text UI framework.
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+
+__all__ = ["App"]
+
+
+from simpleline.logging import setup_logging
+from simpleline.errors import NothingScheduledError
+
+setup_logging()
+
+
+class App():
+ """This is the main class for Simpleline library.
+
+ Do not create instance of this class. Use this class as static!
+ The `initialize()` method must be called before use.
+
+ It is giving you access to the scheduler and event loop. You can have only one instance of this
+ class in your application.
+
+ To create this instance call `App.initialize()` method. This method can also be used to
+ reset settings in the App class to start with new event loop or scheduler.
+ """
+ __app = None
+
+ class AppPimpl():
+
+ def __init__(self, scheduler, event_loop, configuration):
+ self.event_loop = event_loop
+ self.scheduler = scheduler
+ self.configuration = configuration
+
+ @classmethod
+ def initialize(cls, scheduler=None, event_loop=None, global_configuration=None):
+ """Create app instance inside of this class.
+
+ This method can be called multiple times to reset App settings.
+
+ :param scheduler: scheduler used for rendering screens; if not specified use
+ `simpleline.render.screen_scheduler.ScreenScheduler`.
+ :type scheduler: instance of `simpleline.render.screen_scheduler.ScreenScheduler`.
+
+ :param event_loop: event loop used for asynchronous tasks;
+ if not specified use `simpleline.event_loop.main_loop.MainLoop`.
+ :type event_loop: object based on class `simpleline.event_loop.AbstractEventLoop`.
+
+ :param global_configuration: instance of the global configuration object; if not specified
+ use `simpleline.global_configuration.GlobalConfiguration`
+ :type global_configuration: object based on class
+ `simpleline.global_configuration.GlobalConfiguration`
+ """
+ from simpleline.event_loop.main_loop import MainLoop # pylint: disable=import-outside-toplevel
+ from simpleline.render.screen_scheduler import ScreenScheduler # pylint: disable=import-outside-toplevel
+ from simpleline.global_configuration import GlobalConfiguration # pylint: disable=import-outside-toplevel
+
+ if event_loop is None:
+ event_loop = MainLoop()
+ if scheduler is None:
+ scheduler = ScreenScheduler(event_loop)
+ if global_configuration is None:
+ global_configuration = GlobalConfiguration()
+
+ cls.__app = cls.AppPimpl(scheduler, event_loop, global_configuration)
+
+ cls._post_init()
+
+ @classmethod
+ def _post_init(cls):
+ from simpleline.input.input_threading import InputThreadManager # pylint: disable=import-outside-toplevel
+ # FIXME: This should be done by more general way not by calling exact class here.
+ # Create new instance of InputThreadManager because it needs new event loop
+ InputThreadManager.create_new_instance()
+
+ @classmethod
+ def is_initialized(cls):
+ """Is the App already initialized?
+
+ :returns: True if the `App.initialized()` method was called, False otherwise.
+ """
+ if cls.__app is None:
+ return False
+
+ return True
+
+ @classmethod
+ def get_scheduler(cls):
+ """Get instance of class responsible for rendering of the screen."""
+ return cls.__app.scheduler
+
+ @classmethod
+ def get_event_loop(cls):
+ """Get instance of class responsible for processing asynchronous events."""
+ return cls.__app.event_loop
+
+ @classmethod
+ def get_configuration(cls):
+ """Get application defaults configuration object."""
+ return cls.__app.configuration
+
+ @classmethod
+ def run(cls):
+ """Run event loop.
+
+ Raise an exception if no screen is scheduled. This behavior can be changed by
+ `should_run_with_empty_stack` global configuration option.
+
+ This is shortcut to `App.event_loop().run()`.
+ :raises NothingScheduledError: when there is no screen scheduled
+ """
+ if not cls.__app.configuration.should_run_with_empty_stack:
+ # Check if the screen stack is not empty
+ if cls.__app.scheduler.nothing_to_render:
+ raise NothingScheduledError("Can't run application with the empty screen stack! "
+ "To avoid this please see should_run_with_empty_stack "
+ "global configuration option.")
+ App.get_event_loop().run()
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/errors.py b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/errors.py
new file mode 100644
index 0000000..4532c61
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/errors.py
@@ -0,0 +1,31 @@
+# Base exceptions for the Simpleline application.
+#
+# Base class for Simpleline Text UI framework.
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+# This can't be moved to __init__.py because of cyclic imports error.
+#
+
+
+class SimplelineError(Exception):
+ """Base exception for all other exceptions."""
+
+
+class NothingScheduledError(SimplelineError):
+ """Exception when running the loop with no screens scheduled."""
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/event_loop/__init__.py b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/event_loop/__init__.py
new file mode 100644
index 0000000..1b68307
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/event_loop/__init__.py
@@ -0,0 +1,264 @@
+# Abstract base class for Simpleline Event Loop.
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+
+# This class can be overridden to use any existing event loop of your program.
+#
+# Author(s): Jiri Konecny
+#
+
+import sys
+
+from abc import ABCMeta, abstractmethod
+from collections import namedtuple
+
+from simpleline.errors import SimplelineError
+from simpleline.event_loop.ticket_machine import TicketMachine
+from simpleline.logging import get_simpleline_logger
+
+log = get_simpleline_logger()
+
+__all__ = ["AbstractEventLoop", "AbstractSignal", "ExitMainLoop"]
+
+QuitCallback = namedtuple("QuitCallback", ["callback", "args"])
+
+
+class ExitMainLoop(SimplelineError):
+ """This exception ends the whole event loop."""
+
+
+class AbstractEventLoop(metaclass=ABCMeta):
+
+ def __init__(self):
+ super().__init__()
+ self._handlers = {}
+ self._processed_signals = TicketMachine()
+ self._quit_callback = None
+ # end most inner loop politely by setting to False
+ self._run_loop = True
+ self._force_quit = False
+
+ def register_signal_handler(self, signal, callback, data=None):
+ """Register a callback which will be called when message "event"
+ is encountered during process_events.
+
+ The callback has to accept two arguments:
+ - the received message in the form of (type, [arguments])
+ - the data registered with the handler
+
+ :param signal: Signal class we want to react on.
+ :type signal: Class based on the simpleline.event_loop.AbstractSignal class.
+
+ :param callback: The callback function.
+ :type callback: func(event_message, data)
+
+ :param data: Optional data to pass to callback.
+ :type data: Anything.
+ """
+ if signal not in self._handlers:
+ self._handlers[signal] = []
+
+ event_handler = self._create_event_handler(callback, data)
+ self._handlers[signal].append(event_handler)
+
+ @abstractmethod
+ def register_signal_source(self, signal_source):
+ """Register source of signal for actual event queue.
+
+ :param signal_source: Source for future signals.
+ :type signal_source: `simpleline.render.ui_screen.UIScreen`
+ """
+
+ @abstractmethod
+ def enqueue_signal(self, signal):
+ """Enqueue new event for processing.
+
+ :param signal: Signal which you want to add to the event queue for processing.
+ :type signal: Instance based on AbstractEvent class.
+ """
+ log.debug("New signal %s enqueued with source %s",
+ signal,
+ signal.source.__class__.__name__)
+
+ @abstractmethod
+ def run(self):
+ """Starts the event loop."""
+ log.debug("Starting main loop")
+ self._force_quit = False
+
+ def force_quit(self):
+ """Force quit all running event loops.
+
+ Kill all loop including inner loops (modal window).
+ None of the Simpleline events will be processed anymore.
+ """
+ log.debug("Force quit called. Killing all loops!")
+ self._force_quit = True
+
+ @abstractmethod
+ def execute_new_loop(self, signal):
+ """Starts the new event loop and pass `signal` in it.
+
+ This is required for processing a modal screens.
+
+ :param signal: Signal passed to the new event loop.
+ :type signal: The `AbstractSignal` based class.
+ """
+ log.debug("Executing inner loop")
+
+ @abstractmethod
+ def close_loop(self):
+ """Close active event loop.
+
+ Close an event loop created by the `execute_new_loop()` method.
+ """
+ log.debug("Closing inner loop")
+
+ @abstractmethod
+ def process_signals(self, return_after=None):
+ """This method processes incoming async messages.
+
+ Process signals enqueued by the `self.enqueue_signal()` method. Call handlers
+ registered to the signals by the `self.register_signal_handler()` method.
+
+ When `return_after` is specified then wait to the point when this signal is processed.
+ NO warranty that this method will return immediately after the signal was processed!
+
+ Without `return_after` parameter this method will return after all queued signals
+ with the highest priority will be processed.
+
+ The method is NOT thread safe!
+
+ :param return_after: Wait on this signal to be processed.
+ :type return_after: Class of the signal.
+ """
+
+ def set_quit_callback(self, callback, args=None):
+ """Call this callback when event loop quits.
+
+ :param callback: Call this callback when event loops ends (application quit).
+ :type callback: Function with one parameter data `func(data)`.
+
+ :param args: Arguments passed to the quit callback.
+ :type args: Anything.
+ """
+ self._quit_callback = QuitCallback(callback, args)
+
+ def kill_app_with_traceback(self, exception_signal, data=None):
+ """Print exception and screen stack and kill the application.
+
+ :param exception_signal: ExceptionSignal encapsulating the original exception which
+ will be passed to the sys.excepthook method.
+ :type exception_signal: Instance of `simpleline.event_loop.signals.ExceptionSignal` class.
+
+ :param data: To be usable as signal handler.
+ :type data: Anything will be ignored.
+ """
+ log.debug("Unhandled error in handler raised:")
+ sys.excepthook(*exception_signal.exception_info)
+
+ from simpleline import App # pylint: disable=import-outside-toplevel
+ stack_dump = App.get_scheduler().dump_stack()
+ print("")
+ print(stack_dump)
+ log.error(stack_dump)
+
+ log.debug("Killing application!")
+ sys.exit(1)
+
+ @staticmethod
+ def _create_event_handler(callback, data):
+ """Create event handler data object and return it."""
+ return EventHandler(callback=callback, data=data)
+
+ def _register_wait_on_signal(self, wait_on_signal):
+ """Register process waiting on signal `wait_on_signal` and return id for later checking.
+
+ ID is returned which is then used in the `self._check_if_signal_processed()` method.
+
+ :param wait_on_signal: Signal we are waiting for.
+ :type wait_on_signal: Class based on `simpleline.event_loop.AbstractSignal`.
+ """
+ return self._processed_signals.take_ticket(wait_on_signal.__name__)
+
+ def _mark_signal_processed(self, signal):
+ """Mark that processes waiting on this signal that they are able to go.
+
+ :param signal: Signal which was processed.
+ :type signal: Class based on `simpleline.event_loop.AbstractSignal`.
+ """
+ self._processed_signals.mark_line_to_go(signal.__class__.__name__)
+
+ def _check_if_signal_processed(self, wait_on_signal, unique_id):
+ """Check if the signal was processed.
+
+ :param wait_on_signal: Signal the process is waiting for.
+ :type wait_on_signal: Class based on `simpleline.event_loop.AbstractSignal`.
+
+ :param unique_id: Unique id returned by the `self._register_wait_on_signal()` method.
+ :type unique_id: int
+ """
+ return self._processed_signals.check_ticket(wait_on_signal.__name__, unique_id)
+
+
+class EventHandler():
+ """Data class to save event handlers."""
+
+ def __init__(self, callback, data):
+ self.callback = callback
+ self.data = data
+
+
+class AbstractSignal(metaclass=ABCMeta):
+ """This class is base class for signals.
+
+ .. NOTE:
+ Ordering and equality is based on priority.
+ """
+
+ def __init__(self, source, priority=0):
+ self._source = source
+ self._priority = priority
+
+ def __lt__(self, other):
+ """Order Signal classes by priority."""
+ return self._priority < other.priority
+
+ def __eq__(self, other):
+ """Order Signal classes by priority."""
+ return self._priority == other.priority
+
+ def __str__(self):
+ """For easier logging."""
+ return self.__class__.__name__
+
+ @property
+ def priority(self):
+ """Priority of this event.
+
+ Values less than 0 denote higher priorities. Values greater than 0 denote lower priorities.
+ Events from high priority sources are always processed before events from lower priority
+ sources.
+ """
+ return self._priority
+
+ @property
+ def source(self):
+ """Source which emitted this event."""
+ return self._source
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/event_loop/event_queue.py b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/event_loop/event_queue.py
new file mode 100644
index 0000000..c0351d3
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/event_loop/event_queue.py
@@ -0,0 +1,153 @@
+# Default event queue for Simpleline application.
+#
+# This class is thread safe.
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+# Author(s): Jiri Konecny
+#
+
+
+from queue import PriorityQueue
+from threading import Lock
+
+from simpleline.errors import SimplelineError
+
+
+class EventQueueError(SimplelineError):
+ """Main exception for `EventQueue` class.
+
+ Inherits from `simpleline.SimplelineError`.
+ """
+
+
+class EventQueue():
+ """Class for managing signal queue.
+
+ Responsibilities of this class are:
+ * sorting by priority of signals
+ * managing sources for this event queue
+ * enqueuing signals
+ """
+
+ def __init__(self):
+ self._queue = PriorityQueue()
+ self._contained_screens = set()
+ self._lock = Lock()
+
+ def empty(self):
+ """Return true if Queue is empty.
+
+ :return: True if empty, False otherwise.
+ """
+ return self._queue.empty()
+
+ def enqueue(self, signal):
+ """Enqueue signal to this queue.
+
+ :param signal: Signal which should be enqueued to this queue.
+ :type signal: Signal class based on `simpleline.event_loop.signals.AbstractSignal`.
+ """
+ self._queue.put(signal)
+
+ def enqueue_if_source_belongs(self, signal, source):
+ """Enqueue signal to this queue if the signal source belongs to this queue.
+
+ Enqueue the `signal` only if the `source` belongs to this queue.
+ See the `add_source()` method.
+
+ :param signal: Signal which should be enqueued to this queue.
+ :type signal: Signal class based on `simpleline.event_loop.signals.AbstractSignal`.
+ :param source: Source of this signal.
+ :type source: Anything.
+ :return: True if the source belongs to this queue and signal was queued, False otherwise.
+ :rtype: bool
+ """
+ if self.contains_source(source):
+ self._queue.put(signal)
+ return True
+
+ return False
+
+ def get(self):
+ """Return enqueued signal with the highest priority.
+
+ This is FIFO implementation for the same priority.
+ If the queue is empty this method will wait for the input signal.
+
+ :return: Queued signal.
+ :rtype: Signal based on class `simpleline.event_loop.signals.AbstractSignal`.
+ """
+ return self._queue.get()
+
+ def get_top_event_if_priority(self, priority):
+ """Return top enqueued signal if priority is equal to `priority`. Otherwise `None`.
+
+ :param priority: Requested event priority.
+ :type priority: int
+
+ :return: Queued signal if it has requested priority. Otherwise `None`.
+ :rtype: Signal based on class `simpleline.event_loop.signals.AbstractSignal` or `None`.
+ """
+ event = self._queue.get()
+ if event.priority == priority:
+ return event
+
+ self._queue.put(event)
+ return None
+
+ def add_source(self, signal_source):
+ """Add new source of signals to this queue.
+
+ This method is mandatory for `enqueue_if_source_belongs()` method.
+ The same source will be added only once.
+
+ :param signal_source: Source of future signals.
+ :type signal_source: Anything which will emit signals in future.
+ """
+ # TODO: Remove when python3-astroid 1.5.3 will be in Fedora
+ # pylint: disable=not-context-manager
+ with self._lock:
+ self._contained_screens.add(signal_source)
+
+ def remove_source(self, signal_source):
+ """Remove signal source from this queue.
+
+ :param signal_source: Source of future signals.
+ :type signal_source: Anything.
+ :raise: EventQueueError"""
+ try:
+ # TODO: Remove when python3-astroid 1.5.3 will be in Fedora
+ # pylint: disable=not-context-manager
+ with self._lock:
+ self._contained_screens.remove(signal_source)
+ except KeyError as e:
+ raise EventQueueError("Can't remove non-existing event source!") from e
+
+ def contains_source(self, signal_source):
+ """Test if `signal_source` belongs to this queue.
+
+ :param signal_source: Source of signals.
+ :type signal_source: Anything.
+ :return: True if signal source belongs to this queue.
+ :rtype: bool
+ """
+ # TODO: Remove when python3-astroid 1.5.3 will be in Fedora
+ # pylint: disable=not-context-manager
+ with self._lock:
+ return signal_source in self._contained_screens
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/event_loop/glib_event_loop.py b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/event_loop/glib_event_loop.py
new file mode 100644
index 0000000..25847e5
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/event_loop/glib_event_loop.py
@@ -0,0 +1,230 @@
+# Glib event queue used by Simpleline application.
+#
+# This class is thread safe.
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+# Author(s): Jiri Konecny
+#
+from collections import namedtuple
+
+import gi
+
+from simpleline.event_loop import AbstractEventLoop, ExitMainLoop
+from simpleline.event_loop.signals import ExceptionSignal
+from simpleline.logging import get_simpleline_logger
+
+gi.require_version("GLib", "2.0")
+
+from gi.repository import GLib # pylint: disable=wrong-import-order, wrong-import-position
+
+log = get_simpleline_logger()
+
+CallbackArgs = namedtuple("CallbackArgs", ["signal", "source", "handlers"])
+
+
+__all__ = ["GLibEventLoop"]
+
+
+class GLibEventLoop(AbstractEventLoop):
+
+ def __init__(self):
+ super().__init__()
+ # Create first loop
+ loop = GLib.MainLoop()
+ self._event_loops = [EventLoopData(loop)]
+ log.debug("GLib event loop is used!")
+
+ @property
+ def active_main_loop(self):
+ """Return GLib mainloop object."""
+ return self._event_loops[-1].loop
+
+ def register_signal_source(self, signal_source):
+ """Register source of signal for actual event queue.
+
+ :param signal_source: Source for future signals.
+ :type signal_source: `simpleline.render.ui_screen.UIScreen`
+ """
+ super().register_signal_source(signal_source)
+ loop_data = self._event_loops[-1]
+ loop_data.sources.add(signal_source)
+
+ def enqueue_signal(self, signal):
+ """Enqueue new event for processing.
+
+ :param signal: signal which you want to add to the event queue for processing
+ :type signal: instance based on AbstractEvent class
+ """
+ if self._force_quit:
+ return
+
+ super().enqueue_signal(signal)
+
+ loop_data = self._find_loop_data_for_source(signal.source)
+ self._register_handlers_to_loop(loop_data.loop, signal)
+
+ def _find_loop_data_for_source(self, source):
+ """Find event loop belonging to this signal source."""
+ for loop_data in reversed(self._event_loops):
+ if source in loop_data.sources:
+ return loop_data
+
+ return self._event_loops[-1]
+
+ def _register_handlers_to_loop(self, event_loop, signal):
+ """Register handlers to the event loop."""
+ context = event_loop.get_context()
+ handlers = []
+
+ if type(signal) in self._handlers: # pylint: disable=unidiomatic-typecheck
+ handlers = self._handlers[type(signal)]
+ elif isinstance(signal, ExceptionSignal):
+ handler_data = self._create_event_handler(self.kill_app_with_traceback, None)
+ handlers = [handler_data]
+
+ # GLib event source which contains handler callback
+ # Every source can hold only one callback
+ source = GLib.idle_source_new()
+ source.set_priority(signal.priority)
+ data = CallbackArgs(signal, source, handlers)
+
+ source.set_callback(self._run_handlers, data)
+ # attach source to the event loop
+ source.attach(context)
+
+ def _run_handlers(self, data):
+ """Run handlers attached to this signal and clean source afterwards."""
+ signal = data.signal
+ source = data.source
+ handlers = data.handlers
+
+ if not self._force_quit:
+ try:
+ for handler in handlers:
+ handler.callback(signal, handler.data)
+ except ExitMainLoop:
+ self._quit_all_loops()
+ except Exception: # pylint: disable=broad-except
+ self.enqueue_signal(ExceptionSignal(self))
+
+ # based on GLib documentation we should clean source
+ # source will be removed from event loop context this way
+ source.destroy()
+
+ self._mark_signal_processed(signal)
+
+ def _quit_all_loops(self):
+ for loop_data in reversed(self._event_loops):
+ loop_data.loop.quit()
+
+ def run(self):
+ """Starts the event loop."""
+ super().run()
+ if len(self._event_loops) != 1:
+ raise ValueError("Can't run event loop multiple times.")
+
+ self._event_loops[0].loop.run()
+ log.debug("Main loop ended. Running callback if set.")
+
+ if self._quit_callback:
+ cb = self._quit_callback.callback
+ cb(self._quit_callback.args)
+
+ def force_quit(self):
+ """Force quit all running event loops.
+
+ Kill all loop including inner loops (modal window).
+ None of the Simpleline events will be processed anymore.
+ """
+ super().force_quit()
+ self._quit_all_loops()
+
+ def execute_new_loop(self, signal):
+ """Starts the new event loop and pass `signal` in it.
+
+ This is required for processing a modal screens.
+
+ :param signal: signal passed to the new event loop
+ :type signal: `AbstractSignal` based class
+ """
+ super().execute_new_loop(signal)
+
+ if self._force_quit:
+ return
+
+ new_context = GLib.MainContext()
+ new_loop = GLib.MainLoop(new_context)
+ loop_data = EventLoopData(new_loop)
+ self._event_loops.append(loop_data)
+
+ self.enqueue_signal(signal)
+ new_loop.run()
+
+ def close_loop(self):
+ """Close active event loop.
+
+ Close an event loop created by the `execute_new_loop()` method.
+ """
+ super().close_loop()
+ old_loop_data = self._event_loops.pop()
+ old_loop_data.loop.quit()
+
+ def process_signals(self, return_after=None):
+ """This method processes incoming async messages.
+
+ Process signals en-queued by the `self.enqueue_signal()` method. Call handlers registered
+ to the signals by the `self.register_signal_handler()` method.
+
+ When `return_after` is specified then wait to the point when this signal is processed.
+ NO warranty that this method will return immediately after the signal was processed!
+
+ Without `return_after` parameter this method will return after all queued signals with
+ the highest priority will be processed.
+
+ The method is NOT thread safe!
+
+ :param return_after: Wait on this signal to be processed.
+ :type return_after: Class of the signal.
+ """
+ super().process_signals(return_after)
+ loop_data = self._event_loops[-1]
+
+ if return_after is not None:
+ ticket_id = self._register_wait_on_signal(return_after)
+
+ while not self._check_if_signal_processed(return_after, ticket_id) and \
+ not self._force_quit:
+ self._iterate_event_loop(loop_data.loop)
+ else:
+ self._iterate_event_loop(loop_data.loop)
+
+ @staticmethod
+ def _iterate_event_loop(event_loop):
+ context = event_loop.get_context()
+ # This is useful for tests
+ wait_on_timeout = False
+ context.iteration(wait_on_timeout)
+
+
+class EventLoopData():
+
+ def __init__(self, loop):
+ super().__init__()
+ self.loop = loop
+ self.sources = set()
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/event_loop/main_loop.py b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/event_loop/main_loop.py
new file mode 100644
index 0000000..bf64a15
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/event_loop/main_loop.py
@@ -0,0 +1,247 @@
+# Default event loop for Simpleline application.
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+# Author(s): Jiri Konecny
+#
+
+from threading import Lock
+
+from simpleline.event_loop import AbstractEventLoop, ExitMainLoop
+from simpleline.event_loop.event_queue import EventQueue
+from simpleline.event_loop.signals import ExceptionSignal
+from simpleline.logging import get_simpleline_logger
+
+log = get_simpleline_logger()
+
+__all__ = ["MainLoop"]
+
+
+class MainLoop(AbstractEventLoop):
+ """Default main event loop for the Simpleline.
+
+ This event loop can be replaced by your event loop by implementing
+ `simpleline.event_loop.AbstractEventLoop` class.
+ """
+
+ def __init__(self):
+ super().__init__()
+ self._active_queue = EventQueue()
+ self._event_queues = [self._active_queue]
+ self._lock = Lock()
+
+ def register_signal_source(self, signal_source):
+ """Register source of signal for actual event queue.
+
+ :param signal_source: Source for future signals.
+ :type signal_source: `simpleline.render.ui_screen.UIScreen`.
+ """
+ super().register_signal_source(signal_source)
+ self._active_queue.add_source(signal_source)
+
+ def run(self):
+ """This methods starts the application.
+
+ Do not use self.mainloop() directly as run() handles all the required exceptions
+ needed to keep nested mainloop working.
+ """
+ super().run()
+ self._run_loop = True
+
+ try:
+ self._mainloop()
+ except ExitMainLoop:
+ pass
+
+ log.debug("Main loop ended. Running callback if set.")
+
+ if self._quit_callback:
+ cb = self._quit_callback.callback
+ cb(self._quit_callback.args)
+
+ def force_quit(self):
+ """Force quit all running event loops.
+
+ Kill all loop including inner loops (modal window).
+ None of the Simpleline events will be processed anymore.
+ """
+ super().force_quit()
+ self._event_queues.clear()
+ self._run_loop = False
+
+ def execute_new_loop(self, signal):
+ """Starts the new event loop and pass `signal` in it.
+
+ This is required for processing a modal screens.
+
+ :param signal: Signal passed to the new event loop.
+ :type signal: The `AbstractSignal` based class.
+ """
+ super().execute_new_loop(signal)
+
+ if self._force_quit:
+ return
+
+ self._active_queue = EventQueue()
+
+ # TODO: Remove when python3-astroid 1.5.3 will be in Fedora
+ # pylint: disable=not-context-manager
+ with self._lock:
+ self._event_queues.append(self._active_queue)
+
+ self.enqueue_signal(signal)
+ self._mainloop()
+ log.debug("Inner loop is closed")
+
+ def close_loop(self):
+ """Close active event loop.
+
+ Close an event loop created by the `execute_new_loop()` method.
+ """
+ super().close_loop()
+ self.process_signals()
+
+ # TODO: Remove when python3-astroid 1.5.3 will be in Fedora
+ # pylint: disable=not-context-manager
+ with self._lock:
+ self._event_queues.pop()
+ try:
+ self._active_queue = self._event_queues[-1]
+ except IndexError:
+ log.error("No more event queues to work with!")
+ raise ExitMainLoop() # pylint: disable=raise-missing-from
+
+ self._run_loop = False
+
+ def enqueue_signal(self, signal):
+ """Enqueue new event for processing.
+
+ Enqueue signal to the most inner queue (nearest to the active queue) where
+ the `signal.source` belongs.
+ If it belongs nowhere enqueue it to the active one.
+
+ This method is thread safe.
+
+ :param signal: Event which you want to add to the event queue for processing.
+ :type signal: Instance based on AbstractEvent class.
+ """
+ if self._force_quit:
+ return
+
+ super().enqueue_signal(signal)
+ # TODO: Remove when python3-astroid 1.5.3 will be in Fedora
+ # pylint: disable=not-context-manager
+ with self._lock:
+ for queue in reversed(self._event_queues):
+ if queue.enqueue_if_source_belongs(signal, signal.source):
+ return
+
+ self._active_queue.enqueue(signal)
+
+ def _mainloop(self):
+ """Single mainloop. Do not use directly, start the application using run()."""
+ # run infinite loop
+ # this will always wait on input processing or similar so it should not busy waiting
+ while self._run_loop:
+ self._process_signals_loop()
+
+ if not self._force_quit:
+ # set back to True to leave outer loop working
+ self._run_loop = True
+
+ def process_signals(self, return_after=None):
+ """This method processes incoming async messages.
+
+ Process signals en-queued by the `self.enqueue_signal()` method. Call handlers
+ registered to the signals by the `self.register_signal_handler()` method.
+
+ When `return_after` is specified then wait to the point when this signal is processed.
+ NO warranty that this method will return immediately after the signal was processed!
+
+ Without `return_after` parameter this method will return after all queued signals
+ with the highest priority will be processed.
+
+ The method is NOT thread safe!
+
+ :param return_after: Wait on this signal to be processed.
+ :type return_after: Class of the signal.
+ """
+ super().process_signals(return_after)
+ if return_after is not None:
+ self._process_signals_with_return(return_after)
+ else:
+ self._process_signals_iteration()
+
+ def _process_signals_with_return(self, return_after):
+ """Process signals until the return_after signal was processed.
+
+ Or the loop quited.
+ """
+ # get unique ID when waiting for the signal
+ unique_id = self._register_wait_on_signal(return_after)
+
+ while self._run_loop:
+ signal = self._active_queue.get()
+
+ # do the signal processing (call handlers)
+ self._process_signal(signal)
+
+ # was our signal processed if yes, return this method
+ if self._check_if_signal_processed(return_after, unique_id):
+ return
+
+ def _process_signals_iteration(self):
+ """Process queued signal and then return."""
+ priority = None
+
+ while not self._active_queue.empty() and self._run_loop:
+ if priority is None:
+ # take first signal to find out the highest priority in queue
+ signal = self._active_queue.get()
+ priority = signal.priority
+ else:
+ # get signal with this priority only
+ signal = self._active_queue.get_top_event_if_priority(priority)
+
+ # Signal with this priority is not available anymore
+ if signal is None:
+ return
+
+ self._process_signal(signal)
+
+ def _process_signals_loop(self):
+ """Process signal until the event loop quited."""
+ while self._run_loop:
+ signal = self._active_queue.get()
+ self._process_signal(signal)
+
+ def _process_signal(self, signal):
+ log.debug("Processing signal %s", signal)
+
+ self._mark_signal_processed(signal)
+
+ if type(signal) in self._handlers: # pylint: disable=unidiomatic-typecheck
+ for handler_data in self._handlers[type(signal)]:
+ try:
+ handler_data.callback(signal, handler_data.data)
+ except ExitMainLoop: # pylint: disable=try-except-raise
+ raise
+ except Exception: # pylint: disable=broad-except
+ self.enqueue_signal(ExceptionSignal(self))
+ elif isinstance(signal, ExceptionSignal):
+ self.kill_app_with_traceback(signal)
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/event_loop/signals.py b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/event_loop/signals.py
new file mode 100644
index 0000000..dd4a5e3
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/event_loop/signals.py
@@ -0,0 +1,110 @@
+# Set of default signals used inside of widgets.
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+# Author(s): Jiri Konecny
+#
+
+from sys import exc_info
+from simpleline.event_loop import AbstractSignal
+
+__all__ = ["ExceptionSignal", "InputReadySignal", "RenderScreenSignal", "CloseScreenSignal",
+ "InputReceivedSignal"]
+
+
+class ExceptionSignal(AbstractSignal):
+ """Emit this signal when exception is raised.
+
+ This class must be created inside of exception handler or `exception_info` must be specified
+ in creation process.
+
+ If you register handler for this exception then the Simpleline's exception handling
+ is disabled!
+ """
+
+ def __init__(self, source, exception_info=None):
+ """Create exception signal with higher priority (-20) than other signals.
+
+ :param source: source of this signal
+ :type source: class which emits this signal
+
+ :param exception_info: if specified raise your exception, otherwise create exception here;
+ to create exception here it needs to be created inside of exception
+ handler
+ :type exception_info: output of `sys.exc_info()` method
+ """
+ super().__init__(source, priority=-20)
+ if exception_info:
+ self.exception_info = exception_info
+ else:
+ self.exception_info = exc_info()
+
+
+class InputReadySignal(AbstractSignal):
+ """Input from user is ready for processing."""
+ def __init__(self, source, input_handler_source, data, priority=0, success=True):
+ """Store user input inside of this signal
+
+ Read the data from user input in `data` attribute.
+
+ The only way how a user should ask for input is to use InputHandler and inherited classes.
+ The input_handler_source param must be set but this signal instance can be attached to
+ another source object which is registered to a specific event loop.
+
+ If no requester (object who uses InputHandler) is specified then source and
+ input_handler_source will both point to InputHandler instance.
+
+ :param source: Source of this signal.
+ :type source: Any object.
+
+ :param input_handler_source: InputHandler who is asking for input.
+ :type input_handler_source: The `simpleline.input.input_handler.InputHandler` based
+ instance.
+
+ :param data: User input data.
+ :type data: str
+
+ :param priority: Priority of this event.
+ :type priority: Int greater than 0.
+
+ :param success: Was the input successful? True on successful input False otherwise.
+ :type success: bool
+ """
+ super().__init__(source, priority=priority)
+ self.input_handler_source = input_handler_source
+ self.data = data
+ self.success = success
+
+
+class InputReceivedSignal(AbstractSignal):
+ """Raw input received.
+
+ This signal will be further processed and InputReadySignal should be enqueued soon.
+ Most probably you are looking for InputReadySignal instead.
+ """
+ def __init__(self, source, data, priority=0):
+ super().__init__(source, priority=priority)
+ self.data = data
+
+
+class RenderScreenSignal(AbstractSignal):
+ """Render UIScreen to terminal."""
+
+
+class CloseScreenSignal(AbstractSignal):
+ """Close current screen."""
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/event_loop/ticket_machine.py b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/event_loop/ticket_machine.py
new file mode 100644
index 0000000..6234720
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/event_loop/ticket_machine.py
@@ -0,0 +1,81 @@
+# Ticket machine synchronization class.
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+# Author(s): Jiri Konecny
+#
+
+
+class TicketMachine():
+ """Hold signals processed by the event loop if someone wait on them.
+
+ This is useful when recursive process events will skip required signal.
+ """
+
+ def __init__(self):
+ self._lines = {}
+ self._counter = 0
+
+ def take_ticket(self, line_id):
+ """Take ticket (id) and go line (processing events).
+
+ Use `check_ticket` if you are ready to go.
+
+ :param line_id: Line where you are waiting.
+ :type line_id: Anything.
+ """
+ obj_id = self._counter
+ if line_id not in self._lines:
+ self._lines[line_id] = {obj_id: False}
+ else:
+ self._lines[line_id][obj_id] = False
+
+ self._counter += 1
+ return obj_id
+
+ def check_ticket(self, line, unique_id):
+ """Check if you are ready to go.
+
+ If True the unique_id is not valid anymore.
+
+ :param unique_id: Your id used to identify you in the line.
+ :type unique_id: int
+
+ :param line: Line where you are waiting.
+ :type line: Anything.
+
+ :return: True if the ticket was already marked, False otherwise
+ :rtype: bool
+ """
+ if self._lines[line][unique_id]:
+ return self._lines[line].pop(unique_id)
+
+ return False
+
+ def mark_line_to_go(self, line):
+ """All in the `line` are ready to go.
+
+ Mark all tickets in the line as True.
+
+ :param line: Line which should processed.
+ :type line: Anything.
+ """
+ if line in self._lines:
+ our_line = self._lines[line]
+ for key in our_line:
+ our_line[key] = True
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/global_configuration.py b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/global_configuration.py
new file mode 100644
index 0000000..1fa91a7
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/global_configuration.py
@@ -0,0 +1,112 @@
+# Global configuration for the whole application.
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+
+from getpass import getpass
+
+__all__ = ["GlobalConfiguration"]
+
+DEFAULT_WIDTH = 80
+DEFAULT_PASSWORD_FUNC = getpass
+
+
+class GlobalConfiguration():
+ """Class for global configuration of application defaults.
+
+ All stored data are persistent between App.initialize() calls and can be used before this call.
+ """
+
+ def __init__(self):
+ self._width = DEFAULT_WIDTH
+ self._getpass = DEFAULT_PASSWORD_FUNC
+ self._run_with_empty_stack = False
+
+ @property
+ def width(self):
+ """Get width of the application.
+
+ :returns: int
+ """
+ return self._width
+
+ @width.setter
+ def width(self, width):
+ """Set width of the application.
+
+ :param width: Number of characters which can be printed to one line.
+ :type width: int
+ """
+ self._width = width
+
+ def clear_width(self):
+ """Clear user defined width and set the default.
+
+ Default: 80 characters
+ """
+ self._width = DEFAULT_WIDTH
+
+ @property
+ def password_function(self):
+ """Get function to get user passwords from a console.
+
+ :returns: Function with one argument which is text representation of prompt.
+ """
+ return self._getpass
+
+ @password_function.setter
+ def password_function(self, password_func):
+ """Set function to get user passwords from a console.
+
+ :param password_func: Function to get password from a command line.
+ :type password_func: Function with one argument which is text representation of prompt.
+ """
+ self._getpass = password_func
+
+ def clear_password_function(self):
+ """Clear user defined password function and set the default.
+
+ Default: getpass.getpass function
+ """
+ self._getpass = getpass
+
+ @property
+ def should_run_with_empty_stack(self):
+ """Should test on empty screen stack when starting event loop.
+
+ :returns: If False the App.run() call will end with an exception (default), True otherwise.
+ """
+ return self._run_with_empty_stack
+
+ @should_run_with_empty_stack.setter
+ def should_run_with_empty_stack(self, value):
+ """Set if the App.run() call should end with an exception when screen stack is empty.
+
+ This can be valuable when you want to schedule a screen later by an other thread.
+
+ :param value: If False the App.run() call will end with an exception, if True it will
+ run with nothing displayed.
+ """
+ self._run_with_empty_stack = value
+
+ def clear_should_run_with_empty_stack(self):
+ """Clear user defined test to run with an empty screen stack.
+
+ Default: False
+ """
+ self._run_with_empty_stack = False
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/input/__init__.py b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/input/__init__.py
new file mode 100644
index 0000000..e632883
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/input/__init__.py
@@ -0,0 +1,22 @@
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+
+from simpleline.input.input_handler import InputHandler, PasswordInputHandler
+
+__all__ = ["InputHandler", "PasswordInputHandler"]
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/input/input_handler.py b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/input/input_handler.py
new file mode 100644
index 0000000..2528a01
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/input/input_handler.py
@@ -0,0 +1,292 @@
+# Handle user input
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+
+import sys
+
+from simpleline import App
+from simpleline.event_loop.signals import InputReadySignal
+from simpleline.render.widgets import TextWidget
+from simpleline.input.input_threading import InputThreadManager, InputRequest
+
+__all__ = ["InputHandler", "PasswordInputHandler"]
+
+
+class InputHandler():
+
+ def __init__(self, callback=None, source=None):
+ """Class to handle input from the terminal.
+
+ This class is designed to be instantiated on place where it should be used.
+ The main method is `get_input()` which is non-blocking asynchronous call. It can be used
+ as synchronous call be calling the `wait_on_input` method.
+
+ To get result from this class use the `value` property.
+
+ :param callback: You can specify callback which will be called when user give input.
+ :type callback: Callback function with one argument which will be user input.
+
+ :param source: Source of this input. It will be helpful in case of debugging an issue.
+ :type source: Class which will process an input from this InputHandler.
+ """
+ super().__init__()
+ self._input = None
+ self._input_callback = callback
+ self._input_received = False
+ self._input_successful = False
+ self._skip_concurrency_check = False
+ self._source = source
+
+ App.get_event_loop().register_signal_handler(InputReadySignal,
+ self._input_received_handler)
+
+ def _input_received_handler(self, signal, args):
+ if signal.input_handler_source != self:
+ return
+
+ self._input_received = True
+ self._input_successful = signal.success
+
+ if not self._input_successful:
+ return
+
+ self._input = signal.data
+
+ # call async callback
+ if self._input_callback is not None:
+ cb = self._input_callback
+ self._input_callback = None
+
+ cb(self._input)
+
+ @property
+ def value(self):
+ """Return user input.
+
+ :returns: String or None if no is input received.
+ """
+ return self._input
+
+ @property
+ def source(self):
+ """Get source of this input.
+
+ :returns: Anything probably UIScreen.
+ """
+ return self._source
+
+ @property
+ def skip_concurrency_check(self):
+ """Is this InputHandler skipping concurrency check?
+
+ :returns bool
+ """
+ return self._skip_concurrency_check
+
+ @skip_concurrency_check.setter
+ def skip_concurrency_check(self, value):
+ """Set if this InputHandler should skip concurrency check.
+
+ Note if you skip this check, you can have unexpected behavior. Use with caution.
+
+ :param value: True to skip the check, False if not.
+ """
+ self._skip_concurrency_check = value
+
+ def set_callback(self, callback):
+ """Set a callback to get user input asynchronously.
+
+ :param callback: Callback called when user write their input.
+ :type callback: Method with 1 argument which is user input: def cb(user_input)
+ """
+ self._input_callback = callback
+
+ def input_received(self):
+ """Was user input already received?
+
+ :returns: True if yes, False otherwise.
+ """
+ return self._input_received
+
+ def wait_on_input(self):
+ """Blocks execution till the user input is received.
+
+ Events will works as expected during this blocking.
+
+ Please check the `input_successful` method to test the input.
+ """
+ # we already received input from user
+ if self._input_received:
+ return
+
+ while not self._input_received:
+ App.get_event_loop().process_signals(InputReadySignal)
+
+ def input_successful(self):
+ """Was input successful?
+
+ :returns: bool
+ """
+ return self._input_successful
+
+ def get_input(self, prompt):
+ """Use prompt to ask for user input and wait (non-blocking) on user input.
+
+ This is an asynchronous call. If you want to wait for user input then use
+ the `wait_on_input` method. If you want to get results asynchronously then register
+ callback in constructor or by the `set_callback` method.
+
+ Check if user input was already received can be done by the `input_received` method call.
+
+ :param prompt: Ask user what you want to get.
+ :type prompt: String or Prompt instance.
+
+ :returns: User input.
+ :rtype: str
+ """
+ self._clear_input()
+ self._invoke_input_thread(prompt)
+
+ def _invoke_input_thread(self, prompt):
+ thread_object = self.create_thread_object(prompt)
+ InputThreadManager.get_instance().start_input_thread(thread_object,
+ not self._skip_concurrency_check)
+
+ def create_thread_object(self, prompt):
+ """Create thread object containing all the information how to get user input.
+
+ :returns: Instance of class inherited from `simpleline.input.InputThread`.
+ """
+ return InputHandlerRequest(App.get_configuration().width, prompt, self)
+
+ def _clear_input(self):
+ self._input_received = False
+ self._input = None
+
+
+class InputHandlerRequest(InputRequest):
+ """This is thread object to get input from user without blocking main thread."""
+
+ def __init__(self, width, prompt, input_handler):
+ """Create request object to get input in InputThreadManager.
+
+ :param width: Width of the screen prompt.
+ :type width: int
+
+ :param prompt: Input prompt.
+ :type prompt: Instance of `simpleline.render.prompt.Prompt` class.
+
+ :param input_handler: InputHandler instance which created this object.
+ :type input_handler: InputHandler based instance.
+ """
+ super().__init__(input_handler, input_handler.source)
+ self._width = width
+ self._prompt = prompt
+
+ def get_input(self):
+ """This method is responsible for interruptable user input.
+
+ It is expected to be used in a thread started on demand
+ and returns the input via the communication Queue.
+ """
+ # lock acquired, we can run input
+ try:
+ data = self._ask_input()
+ except EOFError:
+ data = ""
+
+ return data
+
+ def text_prompt(self):
+ widget = TextWidget(str(self._prompt))
+ widget.render(self._width)
+ lines = widget.get_lines()
+ return "\n".join(lines) + " "
+
+ def _ask_input(self):
+ text_prompt = self.text_prompt()
+ sys.stdout.write(text_prompt)
+ sys.stdout.flush()
+
+ return self._get_input()
+
+ @staticmethod
+ def _get_input():
+ return input()
+
+
+class PasswordInputHandler(InputHandler):
+
+ def __init__(self, callback=None, source=None):
+ """Class to handle hidden password input from the terminal.
+
+ This class is designed to be instantiated on place where it should be used.
+ The main method is `get_input()` which is non-blocking asynchronous call. It can be used
+ as synchronous call be calling the `wait_on_input` method.
+
+ To get result from this class use the `value` property.
+
+ :param callback: You can specify callback which will be called when user give input.
+ :type callback: Callback function with one argument which will be user input.
+
+ :param source: Source of this input. It will be helpful in case of debugging an issue.
+ :type source: Class which will process an input from this InputHandler.
+ """
+ super().__init__(callback=callback, source=source)
+ self._getpass_func = App.get_configuration().password_function
+
+ def set_pass_func(self, getpass_func):
+ """Set a function for getting passwords."""
+ if not getpass_func:
+ return
+
+ self._getpass_func = getpass_func
+
+ def create_thread_object(self, prompt):
+ """Return PasswordInputThread for getting user password."""
+ return PasswordInputHandlerRequest(App.get_configuration().width, prompt, self,
+ self._getpass_func)
+
+
+class PasswordInputHandlerRequest(InputHandlerRequest):
+ """Similar as InputHandlerRequest but don't echo user keys."""
+
+ def __init__(self, width, prompt, input_handler, getpass_func):
+ """Create request object to get password input in InputThreadManager.
+
+ :param width: Width of the screen prompt.
+ :type width: int
+
+ :param prompt: Input prompt.
+ :type prompt: Instance of `simpleline.render.prompt.Prompt` class.
+
+ :param input_handler: InputHandler instance which created this object.
+ :type input_handler: InputHandler based instance.
+
+ :param getpass_func: Function to get user password.
+ :type getpass_func: Function which gets prompt as only parameter and returns user input
+ string.
+ """
+ super().__init__(width, prompt, input_handler)
+ self._getpass_func = getpass_func
+
+ def _ask_input(self):
+ text_prompt = self.text_prompt()
+
+ return self._getpass_func(text_prompt)
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/input/input_threading.py b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/input/input_threading.py
new file mode 100644
index 0000000..b8926c8
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/input/input_threading.py
@@ -0,0 +1,238 @@
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+
+import threading
+from abc import ABCMeta, abstractmethod
+
+from simpleline import App
+from simpleline.logging import get_simpleline_logger
+from simpleline.event_loop.signals import InputReceivedSignal, InputReadySignal
+
+log = get_simpleline_logger()
+
+
+INPUT_THREAD_NAME = "SimplelineInputThread"
+
+
+class InputThreadManager():
+ """Manager object for input threads.
+
+ This manager helps with concurrent user input (still you really shouldn't do that).
+ """
+
+ __instance = None
+
+ def __init__(self):
+ super().__init__()
+ self._input_stack = []
+ self._processing_input = False
+
+ @classmethod
+ def create_new_instance(cls):
+ instance = InputThreadManager()
+ cls.__instance = instance
+
+ instance._post_init_configuration() # pylint: disable=protected-access
+
+ def _post_init_configuration(self):
+ # pylint: disable=protected-access
+ App.get_event_loop().register_signal_handler(InputReceivedSignal,
+ self.__instance._input_received_handler)
+
+ @classmethod
+ def get_instance(cls):
+ if not cls.__instance:
+ cls.create_new_instance()
+
+ return cls.__instance
+
+ def _input_received_handler(self, signal, args):
+ thread_object = self._input_stack.pop()
+ thread_object.emit_input_ready_signal(signal.data)
+
+ if thread_object.thread:
+ thread_object.thread.join()
+
+ # wait until used object ends
+ for t in self._input_stack:
+ t.emit_failed_input_ready_signal()
+ if t.thread:
+ t.thread.join()
+
+ # remove all other items waiting for input
+ self._input_stack.clear()
+ self._processing_input = False
+
+ def start_input_thread(self, input_thread_object, concurrent_check=True):
+ """Start input thread to get user input.
+
+ :param input_thread_object: Input thread object based on InputThread class.
+ :param concurrent_check: Should the concurrent thread check be fatal? (default True).
+ """
+ self._input_stack.append(input_thread_object)
+ self._check_input_thread_running(concurrent_check)
+ self._start_user_input_async()
+
+ def _check_input_thread_running(self, raise_concurrent_check):
+ if len(self._input_stack) != 1:
+ if not raise_concurrent_check:
+ log.warning("Asking for multiple inputs with concurrent check bypassed, "
+ "last who asked wins! Others are dropped.")
+ else:
+ msg = ""
+ for t in self._input_stack:
+ requester_source = t.requester_source or "Unknown"
+ msg += "Input handler: {} Input requester: {}\n".format(t.source,
+ requester_source)
+
+ msg.rstrip()
+
+ raise KeyError("Can't run multiple input threads at the same time!\n"
+ "Asking for input:\n"
+ "{}".format(msg))
+
+ def _start_user_input_async(self):
+ thread_object = self._input_stack[-1]
+
+ if self._processing_input:
+ self._print_new_prompt(thread_object)
+ return
+
+ thread_object.initialize_thread()
+ self._processing_input = True
+ thread_object.start_thread()
+
+ @staticmethod
+ def _print_new_prompt(thread_object):
+ prompt = thread_object.text_prompt()
+
+ # print new prompt
+ print(prompt, end="")
+
+
+class InputRequest(metaclass=ABCMeta):
+ """Base input request class.
+
+ This should be overloaded for every InputHandler class. Purpose of this class is to print
+ prompt and get input from user.
+
+ The `run_input` method is the entry point for this class. Output from this method must be
+ a user input.
+ The `text_prompt` method is used to get textual representation of a prompt. This will be used
+ on concurrent input to replace existing prompt to get new input.
+
+ WARNING:
+ The `run_input` method will run in a separate thread!
+ """
+
+ def __init__(self, source, requester_source=None):
+ super().__init__()
+ self._source = source
+ self._requester_source = requester_source
+ self.thread = None
+
+ @property
+ def source(self):
+ """Get direct source of this input request.
+
+ :returns: InputHandler instance.
+ """
+ return self._source
+
+ @property
+ def requester_source(self):
+ """Get requester -- source of this input.
+
+ :returns: Anything probably UIScreen based instance.
+ """
+ return self._requester_source
+
+ def emit_input_ready_signal(self, input_data):
+ """Emit the InputReadySignal signal with collected input data.
+
+ :param input_data: Input data received.
+ :type input_data: str
+ """
+ handler_source = self.source
+ signal_source = self._get_request_source()
+
+ new_signal = InputReadySignal(source=signal_source, input_handler_source=handler_source,
+ data=input_data, success=True)
+ App.get_event_loop().enqueue_signal(new_signal)
+
+ def emit_failed_input_ready_signal(self):
+ """Emit the InputReadySignal with failed state."""
+ handler_source = self.source
+ signal_source = self._get_request_source()
+
+ new_signal = InputReadySignal(source=signal_source, input_handler_source=handler_source,
+ data="", success=False)
+ App.get_event_loop().enqueue_signal(new_signal)
+
+ def _get_request_source(self):
+ """Get user input request source.
+
+ That means object who is using InputHandler.
+ If this object is not specified then return InputHandler as a source.
+ """
+ return self.requester_source or self.source
+
+ def initialize_thread(self):
+ """Initialize thread for this input request.
+
+ Do not call this directly! Will be called by InputThreadManager.
+ """
+ self.thread = threading.Thread(name=INPUT_THREAD_NAME, target=self.run)
+ self.thread.daemon = True
+
+ def start_thread(self):
+ """Start input thread.
+
+ Do not call this directly! Will be called by InputThreadManager.
+ """
+ self.thread.start()
+
+ def run(self):
+ """Run the `run_input` method and propagate input outside.
+
+ Do not call this method directly. It will be called by InputThreadManager.
+ """
+ data = self.get_input()
+
+ App.get_event_loop().enqueue_signal(InputReceivedSignal(self, data))
+
+ @abstractmethod
+ def text_prompt(self):
+ """Get text representation of the user prompt.
+
+ This will be used to get high priority input.
+
+ :returns: String representation of the prompt or None if no prompt is present.
+ """
+ return None
+
+ @abstractmethod
+ def get_input(self):
+ """Print prompt and get an input from user.
+
+ ..NOTE: Overload this method in your class.
+
+ Return this input from a function.
+ """
+ return ""
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/logging.py b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/logging.py
new file mode 100644
index 0000000..f22ddaa
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/logging.py
@@ -0,0 +1,39 @@
+# Logging functions and methods used by Simpleline.
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+# Author(s): Jiri Konecny
+#
+
+
+import logging
+
+
+SIMPLELINE_LOGGER = "simpleline"
+
+
+def setup_logging():
+ """Set proper logging for a library"""
+ log = get_simpleline_logger()
+ null_hd = logging.NullHandler()
+ log.addHandler(null_hd)
+
+
+def get_simpleline_logger():
+ """Return logging instance that can be used in the application."""
+ return logging.getLogger(SIMPLELINE_LOGGER)
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/render/__init__.py b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/render/__init__.py
new file mode 100644
index 0000000..20125e6
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/render/__init__.py
@@ -0,0 +1,31 @@
+# Module with classes to handle rendering and input.
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+# Author(s): Jiri Konecny
+#
+
+from simpleline.errors import SimplelineError
+
+
+class RenderError(SimplelineError):
+ """Exception raised when error in rendering happens."""
+
+
+class RenderUnexpectedError(RenderError):
+ """Exception raised when something goes really wrong."""
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/render/adv_widgets.py b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/render/adv_widgets.py
new file mode 100644
index 0000000..e8399b4
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/render/adv_widgets.py
@@ -0,0 +1,253 @@
+# Advanced widgets
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+
+import sys
+
+from simpleline.render import widgets
+from simpleline.render.containers import WindowContainer
+from simpleline.render.prompt import Prompt
+from simpleline.render.screen import UIScreen, InputState
+from simpleline.input.input_handler import PasswordInputHandler
+from simpleline.utils.i18n import _, N_, C_
+
+__all__ = ["ErrorDialog", "GetInputScreen", "GetPasswordInputScreen", "HelpScreen",
+ "PasswordDialog", "YesNoDialog"]
+
+
+class ErrorDialog(UIScreen):
+ """Dialog screen for reporting errors to user."""
+
+ def __init__(self, message):
+ """
+ :param message: the message to show to the user
+ :type message: str
+ """
+ super().__init__()
+ self.title = N_("Error")
+ self._message = message
+
+ def refresh(self, args=None):
+ super().refresh(args)
+ text = widgets.TextWidget(self._message)
+ self.window.add_with_separator(widgets.CenterWidget(text))
+
+ def prompt(self, args=None):
+ return Prompt(_("Press %s to exit") % Prompt.ENTER)
+
+ def input(self, args, key):
+ """This dialog is closed by any input.
+
+ And causes the program to quit.
+ """
+ sys.exit(1)
+
+
+class PasswordDialog(UIScreen):
+ """Dialog screen for password input."""
+
+ def __init__(self, message=None):
+ """
+ :param message: password prompt question
+ :type message: string
+ """
+ super().__init__()
+ self.title = N_("Password")
+ self._message = message or _("Enter your passphrase")
+ self._password = None
+
+ def refresh(self, args=None):
+ super().refresh(args)
+ text = widgets.TextWidget(self._message)
+ self.window.add_with_separator(widgets.CenterWidget(text))
+
+ def prompt(self, args=None):
+ handler = PasswordInputHandler(source=self)
+ if self.password_func:
+ handler.set_pass_func(self.password_func)
+
+ handler.get_input(_("Passphrase: "))
+ handler.wait_on_input()
+
+ if not handler.input_successful():
+ return None
+
+ self._password = handler.value
+
+ # this may seem innocuous, but it's really a giant hack; we should
+ # not be calling close() from prompt(), but the input handling code
+ # in the TUI is such that without this very simple workaround, we
+ # would be forever pelting users with a prompt to enter their pw
+ self.close()
+ return None
+
+ @property
+ def answer(self):
+ """The response can be None (no response) or the password entered."""
+ return self._password
+
+ def input(self, args, key):
+ if key:
+ self._password = key
+ return InputState.PROCESSED_AND_CLOSE
+
+ return InputState.DISCARDED
+
+
+class YesNoDialog(UIScreen):
+ """Dialog screen for Yes - No questions."""
+
+ def __init__(self, message):
+ """
+ :param message: the message to show to the user
+ :type message: unicode
+ """
+ super().__init__()
+ self.title = N_("Question")
+ self._message = message
+ self._response = None
+
+ def refresh(self, args=None):
+ super().refresh(args)
+ text = widgets.TextWidget(self._message)
+ self.window.add_with_separator(widgets.CenterWidget(text))
+
+ def prompt(self, args=None):
+ return Prompt(_("Please respond '%(yes)s' or '%(no)s'") % {
+ # TRANSLATORS: 'yes' as positive reply
+ "yes": C_('TUI|Spoke Navigation', 'yes'),
+ # TRANSLATORS: 'no' as negative reply
+ "no": C_('TUI|Spoke Navigation', 'no')
+ })
+
+ def input(self, args, key):
+ # TRANSLATORS: 'yes' as positive reply
+ if key == C_('TUI|Spoke Navigation', 'yes'):
+ self._response = True
+ return InputState.PROCESSED_AND_CLOSE
+
+ # TRANSLATORS: 'no' as negative reply
+ if key == C_('TUI|Spoke Navigation', 'no'):
+ self._response = False
+ return InputState.PROCESSED_AND_CLOSE
+
+ return InputState.DISCARDED
+
+ @property
+ def answer(self):
+ """The response can be True (yes), False (no) or None (no response)."""
+ return self._response
+
+
+class HelpScreen(UIScreen):
+ """Screen to display a help message."""
+
+ def __init__(self, help_path):
+ """
+ :param help_path: help file name
+ :type help_path: str
+ """
+ super().__init__()
+ self.title = N_("Help")
+ self.help_path = help_path
+
+ def refresh(self, args=None):
+ """ Show the help. """
+ super().refresh(args)
+ help_message = _("The help is not available.")
+
+ if self.help_path:
+ with open(self.help_path, 'r') as f:
+ help_message = f.read()
+
+ self.window.add_with_separator(widgets.TextWidget(help_message))
+
+ def input(self, args, key):
+ """ Handle user input. """
+ return InputState.PROCESSED_AND_CLOSE
+
+ def prompt(self, args=None):
+ return Prompt(_("Press %s to return") % Prompt.ENTER)
+
+
+class GetInputScreen(UIScreen):
+ """Screen for getting user input."""
+
+ def __init__(self, message):
+ """
+ :param message: Prompt printed before user input.
+ :type message: str
+ """
+ super().__init__()
+ self._message = message
+ self._value = None
+ self._conditions = []
+
+ @property
+ def value(self):
+ """User input."""
+ return self._value
+
+ def add_acceptance_condition(self, acceptance_function, args=None):
+ """Add acceptance condition to the conditions list.
+
+ :param acceptance_function: Functions that accepts or rejects a user input.
+ :type acceptance_function: `function(input, args) -> bool` - function which takes
+ user input (string) and arguments (`args`) and return True when
+ input is accepted or False if rejected so we will ask for
+ a new input.
+
+ :param args: Second argument for `acceptance_function` the first one will be user input.
+ :type args: Anything.
+ """
+ self._conditions.append((acceptance_function, args))
+
+ def clear_acceptance_conditions(self):
+ """Clear list of the acceptance conditions."""
+ self._conditions.clear()
+
+ def refresh(self, args=None):
+ super().refresh(args)
+ self._window = WindowContainer()
+
+ def prompt(self, args=None):
+ return Prompt(message=self._message)
+
+ def input(self, args, key):
+ if not self._test_input(key):
+ return InputState.DISCARDED
+
+ self._value = key
+
+ return InputState.PROCESSED_AND_CLOSE
+
+ def _test_input(self, key):
+ for f, args in self._conditions:
+ if not f(key, args):
+ return False
+
+ return True
+
+
+class GetPasswordInputScreen(GetInputScreen):
+ """Screen for getting user password input."""
+
+ def __init__(self, message):
+ super().__init__(message)
+ self.hide_user_input = True
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/render/containers.py b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/render/containers.py
new file mode 100644
index 0000000..fb233b2
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/render/containers.py
@@ -0,0 +1,475 @@
+# Widgets for holding other widgets.
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+
+from math import ceil
+
+from simpleline.render.widgets import Widget, TextWidget, SeparatorWidget
+
+from simpleline.logging import get_simpleline_logger
+
+__all__ = ["ListRowContainer", "ListColumnContainer", "WindowContainer"]
+
+log = get_simpleline_logger()
+
+
+class Container(Widget):
+ """Base class for containers which will do positioning of the widgets."""
+
+ def __init__(self, items=None, numbering=True):
+ """Construct Container.
+
+ :param items: List of items for positioning in this Container. Callback
+ can't be specified this way.
+ :type items: List of items for rendering.
+
+ :param numbering: Enable/disable automatic numbering (labels) for items.
+ Enabled by default (True).
+ :type numbering: bool
+ """
+ super().__init__()
+ self._key_pattern = None
+ self._items = []
+ if items:
+ for i in items:
+ self._items.append(ContainerItem(i))
+
+ if numbering:
+ self._key_pattern = KeyPattern()
+ else:
+ self._key_pattern = None
+
+ @property
+ def size(self):
+ """Return items count."""
+ return len(self._items)
+
+ @property
+ def key_pattern(self):
+ """Return key pattern which will be used for items numbering.
+
+ Will return `None` if not set.
+ """
+ return self._key_pattern
+
+ @key_pattern.setter
+ def key_pattern(self, key_pattern):
+ """Set the key pattern object which will be used for items numbering.
+
+ Setting `None` will stop doing numbering.
+ """
+ self._key_pattern = key_pattern
+
+ def add(self, item, callback=None, data=None):
+ """Add item to the Container.
+
+ :param item: Add item to this container.
+ :type item: Could be item (based on `simpleline.render.widgets.Widget`)
+ or other container (based on `simpleline.render.containers.Container`).
+
+ :param callback: Add callback for this item. This callback will be called when user
+ activate this `item`.
+ :type callback: function ``func(data)``.
+
+ :param data: Data which will be passed to the callback.
+ :param data: Anything.
+
+ :returns: ID of the item in this Container.
+ :rtype: int
+ """
+ self._items.append(ContainerItem(item, callback, data))
+ return len(self._items) - 1
+
+ def process_user_input(self, key):
+ """Process input from the user if any of the items in the list was called.
+
+ This method must be called in `UIScreen.input()` method if list widget should call
+ the callbacks.
+
+ :param key: Key pressed from user.
+ :type key: str
+
+ :returns: True if key was processed. False otherwise.
+ """
+ if not self._key_pattern:
+ return False
+
+ if not isinstance(key, str):
+ return False
+
+ res = self._key_pattern.translate_input_to_widget_id(key)
+ if res is not None and res >= 0:
+ try:
+ item = self._items[res]
+ if item.callback is not None:
+ item.callback(item.data)
+ return True
+ except IndexError: # container widget with this id doesn't exists
+ return False
+
+ return False
+
+ def create_number_label(self, item_id):
+ """Create TextWidget from KeyPattern.
+
+ :param item_id: Create label for item with this id.
+ :type item_id: int
+
+ :returns: Widget with label for the item with item_id.
+ :rtype: `simpleline.render.widgets.TextWidget` instance.
+ """
+ number_widget = TextWidget(self._key_pattern.get_widget_label(item_id))
+ return number_widget
+
+
+class WindowContainer(Container):
+ """Base container for screens.
+
+ This can hold other containers or Widgets for rendering.
+ """
+
+ def __init__(self, title=None):
+ """Construct base container for screens.
+
+ This container doesn't have numbering support. Input other containers in it to
+ allow numbering and input processing.
+
+ :param title: Title line with separator after this title.
+ :type title: str
+ """
+ super().__init__(numbering=False)
+ self._title = title
+
+ def add_with_separator(self, item, callback=None, data=None, blank_lines=1):
+ """Add widget and after widget add blank line.
+
+ This method will call
+ `self.add(item, callback, data)`
+ `self.add_separator(lines)`
+
+ :param item: Add item to this container.
+ :type item: Could be item (based on `simpleline.render.widgets.Widget`)
+ or other container (based on `simpleline.render.containers.Container`).
+
+ :param callback: Add callback for this item. This callback will be called when user
+ activate this `item`.
+ :type callback: function ``func(data)``.
+
+ :param data: Data which will be passed to the callback.
+ :param data: Anything.
+
+ :param blank_lines: How many blank lines should be printed.
+ :type blank_lines: int greater than 0.
+
+ :returns: ID of the item in this Container.
+ :rtype: int
+ """
+ item_id = self.add(item, callback, data)
+ self.add_separator(blank_lines)
+
+ return item_id
+
+ def add_separator(self, lines=1):
+ """Add blank lines between widgets.
+
+ :param lines: How many blank lines should be printed.
+ :type lines: int greater than 0.
+ """
+ self.add(SeparatorWidget(lines))
+
+ @property
+ def title(self):
+ """Title of WindowContainer."""
+ return self._title
+
+ def render(self, width):
+ """Render widgets to it's internal buffer.
+
+ :param width: the maximum width the item can use
+ :type width: int
+
+ :return: nothing
+ """
+ super().render(width)
+
+ # set cursor position to top-left corner
+ self.set_cursor_position(0, 0)
+
+ if self._title:
+ self._draw_title_and_separator(width)
+
+ for item in self._items:
+ widget = item.widget
+ widget.render(width)
+ self.draw(widget)
+
+ def _draw_title_and_separator(self, width):
+ title_widget = TextWidget(self._title)
+ sep = SeparatorWidget()
+
+ title_widget.render(width)
+ sep.render(width)
+
+ self.draw(title_widget)
+ self.draw(sep)
+
+
+class ListRowContainer(Container):
+ """Place widgets in rows automatically.
+
+ Compared to the ColumnWidget this is able to handle word wrapping correctly.
+
+ There is numbering N) automatically for all items. To disable this feature call
+ `self.key_pattern = None`. If you want other numbering then look on `KeyPattern` class.
+
+ Widgets will be placed based on the number of columns in the following way:
+
+ 1) w1 2) w2 3) w3
+ 4) w4 5) w5 6) w6
+ ....
+ """
+
+ def __init__(self, columns, items=None, columns_width=None, spacing=3, numbering=True):
+ """Create ListWidget with specific number of columns.
+
+ :param columns: How many columns we want.
+ :type columns: int, bigger than 0
+
+ :param items: List of items for positioning in this Container. Callback can't be
+ specified this way.
+ :type items: List of items for rendering.
+
+ :param columns_width: Width of every column. If nothing specified the maximum width
+ will be distributed to columns.
+ :type columns_width: int or None
+
+ :param spacing: Set the spacing between columns.
+ :type spacing: int
+
+ :param numbering: Enable/disable automatic numbering (labels) for items.
+ Enabled by default (True).
+ :type numbering: bool
+ """
+ super().__init__(items, numbering)
+ self._columns = columns
+ self._columns_width = columns_width
+ self._spacing = spacing
+ self._numbering_widgets = []
+
+ def render(self, width):
+ """Render widgets to it's internal buffer.
+
+ :param width: the maximum width the item can use
+ :type width: int
+
+ :return: nothing
+ """
+ super().render(width)
+
+ if self._columns_width is None:
+ spaces_between_columns = self._columns - 1
+ sum_spacing = spaces_between_columns * self._spacing
+ self._columns_width = int((width - sum_spacing) / self._columns)
+
+ ordered_map = self._get_ordered_map()
+ lines_per_rows = self._lines_per_every_row(ordered_map)
+
+ # the leftmost empty column
+ col_pos = 0
+
+ for col in ordered_map:
+ row_pos = 0
+
+ # render and draw contents of column
+ for row_id, item_id in enumerate(col):
+ container = self._items[item_id]
+ widget = container.widget
+
+ # set cursor to first line and leftmost empty column
+ self.set_cursor_position(row_pos, col_pos)
+
+ if self._key_pattern is not None:
+ number_widget = self._numbering_widgets[item_id]
+ widget_width = len(number_widget.text)
+ self.draw(number_widget)
+ self.set_cursor_position(row_pos, col_pos + widget_width)
+
+ self.draw(widget, block=True)
+ row_pos = row_pos + lines_per_rows[row_id]
+
+ # recompute the leftmost empty column
+ col_pos = max((col_pos + self._columns_width), self.width) + self._spacing
+
+ def _lines_per_every_row(self, items):
+ self._render_all_items()
+ # call `self._render_and_calculate_lines_per_rows()` method instead
+ lines_per_row = []
+
+ # go through all items and find how many lines we need for each row
+ # printed (because of wrapping)
+ for column_items in items:
+ for row_id, item_id in enumerate(column_items):
+ item = self._items[item_id]
+ if len(lines_per_row) <= row_id:
+ lines_per_row.append(0)
+
+ lines_per_row[row_id] = max(lines_per_row[row_id], len(item.widget.get_lines()))
+
+ return lines_per_row
+
+ def _render_all_items(self):
+ for item_id, item in enumerate(self._items):
+ item_width = self._columns_width
+
+ if item_width <= 0:
+ raise ValueError("Widget can't be rendered! Columns width is too small.")
+
+ if self._key_pattern:
+ number_widget = self.create_number_label(item_id)
+ # render numbers before widgets
+ number_width = len(number_widget.text)
+ number_widget.render(number_width)
+ self._numbering_widgets.append(number_widget)
+ # reduce the size of widget because of the number
+ item_width -= number_width
+
+ if item_width <= 0:
+ raise ValueError("Widget can't be rendered with numbering on! "
+ "Increase column width or disable numbering.")
+
+ item.widget.render(item_width)
+
+ def _get_ordered_map(self):
+ """Return list of identifiers (index) to the original item list.
+
+ .. NOTE: Use of ``self._prepare_list()` is encouraged to create output list and
+ just fill up this list.
+ """
+ # create list of columns (lists)
+ ordering_map = self._prepare_list()
+
+ for item_id in range(self.size):
+ ordering_map[item_id % self._columns].append(item_id)
+
+ return ordering_map
+
+ def _prepare_list(self):
+ """Prepare list for items ordering to rows and columns.
+
+ List will be prepared as ([column 1], [column 2], ...)
+ """
+ return list(map(lambda x: [], range(0, self._columns)))
+
+
+class ListColumnContainer(ListRowContainer):
+ """Place widgets in columns automatically.
+
+ Compared to the ColumnWidget this is able to handle word wrapping correctly.
+
+ There is numbering N) automatically for all items. To disable this feature call
+ `self.key_pattern = None`. If you want other numbering then look on `KeyPattern` class.
+
+ Widgets will be placed based on the number of columns in the following way:
+
+ 1) w1 4) w4 7) w7
+ 2) w2 5) w5 8) w8
+ 3) w3 6) w6 9) w9
+ """
+
+ def _get_ordered_map(self):
+ ordering_map = self._prepare_list()
+ items_in_column = ceil(len(self._items) / self._columns)
+
+ for item_id in range(self.size):
+ col_position = int(item_id // items_in_column)
+ ordering_map[col_position].append(item_id)
+
+ return ordering_map
+
+
+class KeyPattern():
+ """Pattern for automatic key printing before items."""
+
+ def __init__(self, pattern="{:d}) ", offset=1):
+ """Create the pattern class.
+
+ For enabling greater functionality than python 3 format is able to do, feel free to
+ override this class and use your subclass instead.
+
+ :param pattern: Set pattern which will be called for every item.
+ :type pattern: Strings format method.
+ See https://docs.python.org/3.3/library/string.html#format-string-syntax.
+
+ :param offset: Set the offset for numbering items. Default is 1 to start indexing
+ naturally for user.
+ :type offset: int
+ """
+ self._pattern = pattern
+ self._offset = offset
+
+ def get_widget_label(self, item_id):
+ """Get widget identifier for user input description.
+
+ It should be something similar to the pattern.
+
+ :param item_id: Position of the widget in the list.
+ :type item_id: int starts from 0.
+ """
+ return self._pattern.format(item_id + self._offset)
+
+ def translate_input_to_widget_id(self, user_input):
+ """Get id of the widget from the user input.
+
+ This is reverse translation to `self.get_widget_identifier()`.
+
+ :param user_input: Input from user:
+ :type user_input: str
+
+ :return: ID of the widget in the list or None if the input can't be translated.
+ :rtype: int or None
+ """
+ try:
+ return int(user_input) - 1
+ except ValueError:
+ log.debug("No callback registered for user input %s", user_input)
+ return None
+
+
+class ContainerItem():
+ """Item used inside of containers to store widgets callbacks and data.
+
+ Internal representation for Containers. Do not use this class directly.
+ """
+
+ def __init__(self, widget, callback=None, data=None):
+ """Construct WidgetContainer.
+
+ :param widget: Any item from `simpleline.render.widgets` or `Container`.
+ :type widget: Class subclassing the `simpleline.render.widgets.Widget` class
+ or `simpleline.render.containers.Container`.
+
+ :param callback: This callback will be called as reaction on user input.
+ :type callback: Function with one data parameter: `def func(data):`.
+
+ :param data: Params which will be passed to callback.
+ :type data: Anything.
+ """
+ self.widget = widget
+ self.callback = callback
+ self.data = data
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/render/prompt.py b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/render/prompt.py
new file mode 100644
index 0000000..4c99e35
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/render/prompt.py
@@ -0,0 +1,154 @@
+# Class for the Anaconda TUI prompt.
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+# Author(s): Vendula Poncova
+#
+import logging
+
+from simpleline.utils.i18n import N_, _
+
+log = logging.getLogger("simpleline")
+
+
+class Prompt():
+ """Class to create a prompt message with options."""
+ # Default message of the prompt
+ DEFAULT_MESSAGE = N_("Please make a selection from the above")
+
+ # String to use in a prompt when we want users to press the key ENTER.
+ ENTER = N_("ENTER")
+
+ # TRANSLATORS: 'q' to quit
+ QUIT_DESCRIPTION = N_("to quit")
+ QUIT = 'q'
+
+ # TRANSLATORS:'c' to continue
+ CONTINUE_DESCRIPTION = N_("to continue")
+ CONTINUE = 'c'
+
+ # TRANSLATORS:'r' to refresh
+ REFRESH_DESCRIPTION = N_("to refresh")
+ REFRESH = 'r'
+
+ # TRANSLATORS:'h' to help
+ HELP_DESCRIPTION = N_("to help")
+ HELP = 'h'
+
+ def __init__(self, message=DEFAULT_MESSAGE):
+ """
+ :param message: the message of the prompt
+ :type message: str|None
+ """
+ self.message = message
+ self.options = dict()
+
+ def set_message(self, message):
+ """Set the prompt message.
+
+ :param message: the message of the prompt
+ :type message: str|None
+ """
+ self.message = message
+
+ def add_option(self, key, description):
+ """Add an option to the prompt.
+ Causes a warning if the option already exists.
+
+ :param key: the key for choosing the option
+ :type key: str
+
+ :param description: the description of the option
+ :type description: str
+ """
+ if key in self.options:
+ log.warning("The option '%s' does already exist in '%s'.", key, self)
+
+ self.options[key] = description
+
+ def update_option(self, key, description):
+ """Update an option in the prompt.
+ Causes a warning if the option does not exist.
+
+ :param key: the key for choosing the option
+ :type key: str
+
+ :param description: the description of the option
+ :type description: str
+ """
+ if key not in self.options:
+ log.warning("The option '%s' does not exist in '%s'.", key, self)
+
+ self.options[key] = description
+
+ def add_refresh_option(self, description=REFRESH_DESCRIPTION):
+ """Add the option to refresh."""
+ if Prompt.REFRESH in self.options:
+ self.update_option(Prompt.REFRESH, description)
+ else:
+ self.add_option(Prompt.REFRESH, description)
+
+ def add_continue_option(self, description=CONTINUE_DESCRIPTION):
+ """Add the option to continue."""
+ if Prompt.CONTINUE in self.options:
+ self.update_option(Prompt.CONTINUE, description)
+ else:
+ self.add_option(Prompt.CONTINUE, description)
+
+ def add_quit_option(self, description=QUIT_DESCRIPTION):
+ """Add the option to quit."""
+ if Prompt.QUIT in self.options:
+ self.update_option(Prompt.QUIT, description)
+ else:
+ self.add_option(Prompt.QUIT, description)
+
+ def add_help_option(self, description=HELP_DESCRIPTION):
+ """Add the option to help."""
+ if Prompt.HELP in self.options:
+ self.update_option(Prompt.HELP, description)
+ else:
+ self.add_option(Prompt.HELP, description)
+
+ def remove_option(self, key):
+ """Remove an option with the given key.
+
+ :param key: the key of the option
+ :type key: str
+
+ :return: the removed option
+ :rtype: str|None
+ """
+ return self.options.pop(key, None)
+
+ def __str__(self):
+ """Return the string representation of the prompt."""
+ if not self.message and not self.options:
+ return ""
+
+ parts = []
+
+ if self.message:
+ parts.append(_(self.message))
+
+ if self.options:
+ opt_list = ["'%s' %s" % (key, _(self.options[key]))
+ for key in sorted(self.options.keys())]
+ opt_str = "[%s]" % ", ".join(opt_list)
+ parts.append(opt_str)
+
+ return " ".join(parts) + ": "
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/render/screen/__init__.py b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/render/screen/__init__.py
new file mode 100644
index 0000000..6918b28
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/render/screen/__init__.py
@@ -0,0 +1,313 @@
+# Base class for text window screens.
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+# Author(s): Jiri Konecny
+#
+
+from enum import Enum
+
+from simpleline import App
+from simpleline.render.containers import WindowContainer
+from simpleline.render.prompt import Prompt
+from simpleline.render.screen.signal_handler import SignalHandler
+from simpleline.render.screen.input_manager import InputManager
+from simpleline.utils.i18n import _
+
+__all__ = ["UIScreen", "InputState"]
+
+
+class UIScreen(SignalHandler):
+ """Base class representing one TUI Screen.
+
+ Shares some API with anaconda's GUI to make it easy for devs to create similar UI
+ with the familiar API.
+ """
+
+ def __init__(self, title=None, screen_height=30):
+ """ Constructor of the TUI screen.
+
+ :param title: Title line of the screen.
+ :type title: str
+
+ :param screen_height: height of the screen (useful for printing long widgets)
+ :type screen_height: int (the value must be bigger than 4)
+ """
+ self._title = title
+ self._screen_height = screen_height
+ self._screen_ready = False
+
+ # ask for password
+ self._hide_user_input = False
+ self._password_func = None
+
+ # do not print separator for this screen
+ self._no_separator = False
+
+ # list that holds the content to be printed out
+ self._window = WindowContainer(self.title)
+
+ # should the input be required after draw
+ self._input_required = True
+
+ # index of the page (subset of screen) shown during show_all
+ # indexing starts with 0
+ self._page = 0
+
+ self._input_manager = InputManager(ui_screen=self)
+
+ def __str__(self):
+ """For easier logging."""
+ return self.__class__.__name__
+
+ @property
+ def title(self):
+ """Screen title."""
+ return self._title
+
+ @title.setter
+ def title(self, title):
+ """Set screen title.
+
+ Set `None` to remove title.
+ """
+ self._title = title
+
+ @property
+ def password_func(self):
+ """Get password function.
+
+ This is function with one argument to get password from command line.
+ """
+ return self._password_func
+
+ @password_func.setter
+ def password_func(self, value):
+ """Set password function.
+
+ :param value: Function to get password from a command line.
+ :type value: Function with one argument which is text representation of prompt.
+ """
+ self._password_func = value
+
+ @property
+ def screen_ready(self):
+ """This screen is ready for use."""
+ return self._screen_ready
+
+ @screen_ready.setter
+ def screen_ready(self, screen_ready):
+ """Set ready status for this screen."""
+ self._screen_ready = screen_ready
+
+ @property
+ def input_required(self):
+ """Return if the screen requires input."""
+ return self._input_required
+
+ @input_required.setter
+ def input_required(self, input_required):
+ """Set if the screen should require input."""
+ self._input_required = input_required
+
+ @property
+ def no_separator(self):
+ """Should we print separator for this screen?
+
+ :returns: True to print separator before this screen (default).
+ False do not print separator.
+ """
+ return self._no_separator
+
+ @no_separator.setter
+ def no_separator(self, no_separator):
+ """Print or do not print separator.
+
+ :param no_separator: Specify if the separator should be printed.
+ :type no_separator: bool (default: False).
+ """
+ self._no_separator = no_separator
+
+ @property
+ def hide_user_input(self):
+ """Hide typed user input.
+
+ This is main solution how to ask for password.
+
+ :returns: True if user input should be hidden.
+ False otherwise (default).
+ """
+ return self._hide_user_input
+
+ @hide_user_input.setter
+ def hide_user_input(self, hide_input):
+ """Should be the user input hidden.
+
+ :param hide_input: True if user input should be hidden.
+ False if not (default).
+ :type hide_input: bool (default: False).
+ """
+ self._hide_user_input = hide_input
+
+ @property
+ def window(self):
+ """Return WindowContainer instance."""
+ return self._window
+
+ @window.setter
+ def window(self, window):
+ """Set base WindowContainer instance.
+
+ :param window: Base window container containing other widgets and containers.
+ :type window: Instance of `simpleline.render.containers.WindowContainer` class.
+ """
+ self._window = window
+
+ def get_user_input(self, message, hidden=False):
+ """Get immediately input from the user.
+
+ Use this with cautious. Never call this in middle of rendering or when other
+ input is already waiting. It is recommended to use `self.input_required` instead.
+
+ :param message: Message prompt for the user.
+ :type message: str
+
+ :param hidden: Do not echo user input (password typing).
+ :type hidden: bool
+ """
+ return self._input_manager.get_input_blocking(message, hidden)
+
+ def setup(self, args):
+ """Do additional setup right before this screen is used.
+
+ It is mandatory to call this ancestor method in the child class to set ready status.
+
+ :param args: arguments for the setup
+ :type args: array of values
+ :return: whether this screen should be scheduled or not
+ :rtype: bool
+ """
+ self._screen_ready = True
+ App.get_event_loop().register_signal_source(self)
+ return True
+
+ def refresh(self, args=None):
+ """Method which prepares the content desired on the screen to `self.window`.
+
+ :param args: optional argument passed from switch_screen calls
+ :type args: anything
+ """
+ self.window = WindowContainer(self._title)
+
+ def _print_widget(self, widget):
+ """Prints a widget with user interaction (when needed).
+
+ Could be longer than the screen height.
+
+ :param widget: widget to print
+ :type widget: Widget instance
+ """
+ # TODO: Work even for lower screen_height than 4
+ pos = 0
+ lines = widget.get_lines()
+ num_lines = len(lines)
+
+ if num_lines == 0:
+ return
+
+ prompt_height = 2
+ real_screen_height = self._screen_height - prompt_height
+
+ if num_lines < real_screen_height:
+ # widget plus prompt are shorter than screen height, just print the widget
+ print(u"\n".join(lines))
+ return
+
+ # long widget, print it in steps and prompt user to continue
+ last_line = num_lines - 1
+ while pos <= last_line:
+ if pos + real_screen_height > last_line:
+ # enough space to print the rest of the widget plus regular
+ # prompt (2 lines)
+ for line in lines[pos:]:
+ print(line)
+ pos += self._screen_height - 1
+ else:
+ # print part with a prompt to continue
+ for line in lines[pos:(pos + real_screen_height)]:
+ print(line)
+ custom_prompt = Prompt(_("\nPress %s to continue") % Prompt.ENTER)
+ self._ask_user_input_blocking(custom_prompt)
+ pos += real_screen_height
+
+ def _ask_user_input_blocking(self, prompt):
+ return self._input_manager.get_input_blocking(prompt, False)
+
+ def show_all(self):
+ """Print WindowContainer in `self.window` with all its content."""
+ self.window.render(App.get_configuration().width)
+ self._print_widget(self.window)
+
+ def input(self, args, key):
+ """Method called to process input. If the input is not handled here, return it.
+
+ :param key: input string to process
+ :type key: str
+ :param args: optional argument passed from switch_screen calls
+ :type args: anything
+ :return: return `simpleline.render.InputState.PROCESSED` if key was handled,
+ `simpleline.render.InputState.DISCARDED` if the screen should not process input
+ on the scheduler and key if you want it to.
+ :rtype: `simpleline.render.InputState` enum | str
+ """
+ return key
+
+ def get_input_with_error_check(self, args):
+ """Get user input and redraw if user add too many invalid inputs.
+
+ This method should be used only by ScreenScheduler.
+
+ :param args: Arguments passed in when scheduling this screen.
+ :type args: Anything.
+ """
+ self._input_manager.get_input(args=args)
+
+ def prompt(self, args=None):
+ """Return the text to be shown as prompt or handle the prompt and return None.
+
+ :param args: optional argument passed from switch_screen calls
+ :type args: anything
+ :return: returns an instance of Prompt with text to be shown next to the prompt
+ for input or None to skip further input processing
+ :rtype: Prompt instance|None
+ """
+ prompt = Prompt()
+ prompt.add_refresh_option()
+ prompt.add_continue_option()
+ prompt.add_quit_option()
+ return prompt
+
+ def closed(self):
+ """Callback when this screen is closed."""
+
+
+class InputState(Enum):
+ PROCESSED = 1
+ PROCESSED_AND_REDRAW = 2
+ PROCESSED_AND_CLOSE = 3
+ DISCARDED = 0
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/render/screen/input_manager.py b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/render/screen/input_manager.py
new file mode 100644
index 0000000..dc0c220
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/render/screen/input_manager.py
@@ -0,0 +1,203 @@
+# Class for managing input and output for application.
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+# Author(s): Jiri Konecny
+#
+
+from enum import Enum
+
+from simpleline import App
+from simpleline.event_loop import ExitMainLoop
+from simpleline.event_loop.signals import ExceptionSignal
+from simpleline.render.prompt import Prompt
+from simpleline.input import InputHandler, PasswordInputHandler
+
+from simpleline.logging import get_simpleline_logger
+
+log = get_simpleline_logger()
+
+
+class InputManager():
+
+ def __init__(self, ui_screen):
+ """Processor for user input.
+
+ This class is mainly helper class for ScreenScheduler.
+
+ :param ui_screen: Screen associated with this input manager.
+ :type ui_screen: The `simpleline.render.screen.UIScreen` based instance.
+ """
+ super().__init__()
+ self._ui_screen = ui_screen
+ self._input_error_counter = 0
+ self._input_error_threshold = 5
+ self._input_args = None
+
+ @property
+ def input_error_counter(self):
+ """Return how many times the user provided bad input."""
+ return self._input_error_counter
+
+ @property
+ def input_error_threshold_exceeded(self):
+ """Did the error counter pass the threshold?
+
+ The screen should be redraw.
+ """
+ errors = self._input_error_counter % self._input_error_threshold
+ return errors == 0
+
+ def get_input_blocking(self, message, hidden):
+ """Get blocking input from the user.
+
+ :param message: Message prompt for the user.
+ :type message: str
+
+ :param hidden: Do not echo user input (password typing).
+ :type hidden: bool
+ """
+ if hidden:
+ handler = PasswordInputHandler(source=self)
+ if self._ui_screen.password_func:
+ handler.set_pass_func(self._ui_screen.password_func)
+ else:
+ handler = InputHandler(source=self)
+
+ handler.get_input(message)
+ handler.wait_on_input()
+ return handler.value
+
+ def get_input(self, args=None):
+ """Get input from user.
+
+ :param args: Arguments passed in when UIScreen was scheduled.
+ :type args: Anything.
+ """
+ prompt = self._ui_screen.prompt(args)
+ if not self._is_input_expected(prompt):
+ return
+
+ self._input_args = args
+
+ if not self._ui_screen.hide_user_input:
+ handler = InputHandler(source=self._ui_screen)
+ else:
+ handler = PasswordInputHandler(source=self._ui_screen)
+ if self._ui_screen.password_func:
+ handler.set_pass_func(self._ui_screen.password_func)
+
+ handler.set_callback(self.process_input)
+ handler.get_input(prompt)
+
+ def _is_input_expected(self, prompt):
+ """Check if user handled input processing some other way.
+
+ Do nothing if user did handled user input.
+
+ :returns: True if prompt is set and we can use it to get user input.
+ False if prompt is not available, which means that user handled input on their
+ own.
+ """
+ # None means prompt handled the input by itself -> continue
+ if prompt is None:
+ self._input_error_counter = 0
+ return False
+
+ return True
+
+ def process_input(self, user_input):
+ """Process input from the screens.
+
+ :param user_input: User input string.
+ :type user_input: String.
+
+ :raises: ExitMainLoop or any other kind of exception from screen processing.
+ """
+ # process the input, if it wasn't processed (valid)
+ # increment the error counter
+ try:
+ result = self._process_input(user_input)
+ except ExitMainLoop: # pylint: disable=try-except-raise
+ raise
+ except Exception: # pylint: disable=broad-except
+ App.get_event_loop().enqueue_signal(ExceptionSignal(self))
+ return
+
+ if result.was_successful():
+ self._input_error_counter = 0
+ else:
+ self._input_error_counter += 1
+
+ App.get_scheduler().process_input_result(result, self.input_error_threshold_exceeded)
+
+ def _process_input(self, key):
+ """Method called internally to process unhandled input key presses.
+
+ :param key: The string entered by user.
+ :type key: String.
+
+ :return: Return state result object.
+ :rtype: `simpleline.render.in_out_manager.UserInputResult` class.
+
+ :raises: Anything the Screen can raise in the input processing.
+ """
+ from simpleline.render.screen import InputState # pylint: disable=import-outside-toplevel
+ # delegate the handling to active screen first
+ key = self._ui_screen.input(self._input_args, key)
+ if key == InputState.PROCESSED:
+ return UserInputAction.NOOP
+
+ if key == InputState.PROCESSED_AND_REDRAW:
+ return UserInputAction.REDRAW
+
+ if key == InputState.PROCESSED_AND_CLOSE:
+ return UserInputAction.CLOSE
+
+ if key == InputState.DISCARDED:
+ return UserInputAction.INPUT_ERROR
+
+ # global refresh command
+ if key == Prompt.REFRESH:
+ return UserInputAction.REDRAW
+
+ # global close command
+ if key == Prompt.CONTINUE:
+ return UserInputAction.CLOSE
+
+ # global quit command
+ if key == Prompt.QUIT:
+ return UserInputAction.QUIT
+
+ if key is None:
+ log.warning("Returned key from screen is None. "
+ "This could be missing return in a screen input method?")
+
+ return UserInputAction.INPUT_ERROR
+
+
+class UserInputAction(Enum):
+ """Store user input result."""
+ INPUT_ERROR = -1
+ NOOP = 0
+ REDRAW = 5
+ CLOSE = 6
+ QUIT = 7
+
+ def was_successful(self):
+ return self != UserInputAction.INPUT_ERROR
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/render/screen/signal_handler.py b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/render/screen/signal_handler.py
new file mode 100644
index 0000000..e5e1eb5
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/render/screen/signal_handler.py
@@ -0,0 +1,94 @@
+# Signal handler is giving ability connect and emit to all widgets.
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+# Author(s): Jiri Konecny
+#
+
+from simpleline import App
+from simpleline.event_loop.signals import RenderScreenSignal, CloseScreenSignal
+
+
+class SignalHandler():
+ """Provides methods for handling signals anc callbacks.
+
+ Provides main methods:
+ `connect()` -- connect this widget on given signal
+ `create_signal()` -- create signal class which can be used in the emit method
+ `emit()` -- emit signal given signal
+ """
+
+ def connect(self, signal, callback, data=None):
+ """Connect this class method with given signal.
+
+ :param signal: signal class which you want to connect
+ :type signal: class based on `simpleline.event_loop.AbstractSignal`
+
+ :param callback: the callback function
+ :type callback: func(event_message, data)
+
+ :param data: Data you want to pass to the callback
+ :type data: Anything
+ """
+ App.get_event_loop().register_signal_handler(signal, callback, data)
+
+ def create_signal(self, signal_class, priority=0):
+ """Create signal instance usable in the emit method.
+
+ :param signal_class: signal you want to use
+ :type signal_class: class based on `simpleline.event_loop.AbstractSignal`
+
+ :param priority: priority of the signal; please look on the
+ `simpleline.event_loop.AbstractSignal.priority` for further info
+ :type priority: int
+ """
+ return signal_class(self, priority)
+
+ def emit(self, signal):
+ """Emit the signal.
+
+ This will add `signal` to the event loop.
+
+ :param signal: signal to emit
+ :type signal: instance of class based on `simpleline.event_loop.AbstractSignal`
+ """
+ App.get_event_loop().enqueue_signal(signal)
+
+ def create_and_emit(self, signal):
+ """Create the signal and emit it.
+
+ This is basically shortcut for calling `self.create_signal` and `self.emit`.
+ """
+ created_signal = self.create_signal(signal)
+ self.emit(created_signal)
+
+ def redraw(self):
+ """Emit signal to initiate draw.
+
+ Add RenderScreenSignal to the event loop.
+ """
+ signal = self.create_signal(RenderScreenSignal)
+ App.get_event_loop().enqueue_signal(signal)
+
+ def close(self):
+ """Emit signal to close this screen.
+
+ Add CloseScreenSignal to the event loop.
+ """
+ signal = self.create_signal(CloseScreenSignal)
+ App.get_event_loop().enqueue_signal(signal)
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/render/screen_handler.py b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/render/screen_handler.py
new file mode 100644
index 0000000..19655f7
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/render/screen_handler.py
@@ -0,0 +1,58 @@
+# Serves shortcuts for easy screen scheduling.
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+# Author(s): Jiri Konecny
+#
+
+from simpleline import App
+
+
+class ScreenHandler():
+
+ @classmethod
+ def schedule_screen(cls, ui_screen, args=None):
+ """Schedule screen to the active scheduler.
+
+ See: `simpleline.render.screen_scheduler.schedule_screen()`.
+ """
+ App.get_scheduler().schedule_screen(ui_screen=ui_screen, args=args)
+
+ @classmethod
+ def replace_screen(cls, ui_screen, args=None):
+ """Schedule screen to the active scheduler.
+
+ See: `simpleline.render.screen_scheduler.replace_screen()`.
+ """
+ App.get_scheduler().replace_screen(ui_screen=ui_screen, args=args)
+
+ @classmethod
+ def push_screen(cls, ui_screen, args=None):
+ """Schedule screen to the active scheduler.
+
+ See: `simpleline.render.screen_scheduler.push_screen()`.
+ """
+ App.get_scheduler().push_screen(ui_screen=ui_screen, args=args)
+
+ @classmethod
+ def push_screen_modal(cls, ui_screen, args=None):
+ """Schedule screen to the active scheduler.
+
+ See: `simpleline.render.screen_scheduler.push_screen_modal()`.
+ """
+ App.get_scheduler().push_screen_modal(ui_screen=ui_screen, args=args)
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/render/screen_scheduler.py b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/render/screen_scheduler.py
new file mode 100644
index 0000000..cfc4cbf
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/render/screen_scheduler.py
@@ -0,0 +1,311 @@
+# Class handling rendering of the screens to console.
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+# Author(s): Jiri Konecny
+#
+
+import threading
+
+
+from simpleline import App
+from simpleline.event_loop import ExitMainLoop
+from simpleline.event_loop.signals import ExceptionSignal, RenderScreenSignal, CloseScreenSignal
+from simpleline.render import RenderUnexpectedError
+from simpleline.render.screen.input_manager import UserInputAction
+from simpleline.render.screen_stack import ScreenStack, ScreenData, ScreenStackEmptyException
+
+from simpleline.logging import get_simpleline_logger
+
+log = get_simpleline_logger()
+
+RAW_INPUT_LOCK = threading.Lock()
+
+
+__all__ = ["ScreenScheduler"]
+
+
+class ScreenScheduler():
+
+ def __init__(self, event_loop, scheduler_stack=None):
+ """Constructor where you can pass your own scheduler stack.
+
+ The ScreenStack will be used automatically if scheduler stack will be None.
+
+ :param event_loop: Event loop used for the scheduler.
+ :type event_loop: Class based on `simpleline.event_loop.AbstractEventLoop`.
+ :param scheduler_stack: Use custom scheduler stack if you need to.
+ :type scheduler_stack: `simpleline.screen_stack.ScreenStack` based class.
+ """
+ self._quit_screen = None
+ self._event_loop = event_loop
+
+ if scheduler_stack:
+ self._screen_stack = scheduler_stack
+ else:
+ self._screen_stack = ScreenStack()
+ self._register_handlers()
+
+ self._first_screen_scheduled = False
+
+ @staticmethod
+ def _spacer():
+ return "\n".join(2 * [App.get_configuration().width * "="])
+
+ def _register_handlers(self):
+ self._event_loop.register_signal_handler(RenderScreenSignal, self._process_screen_callback)
+ self._event_loop.register_signal_handler(CloseScreenSignal, self._close_screen_callback)
+
+ @property
+ def quit_screen(self):
+ """Return quit UIScreen."""
+ return self._quit_screen
+
+ @quit_screen.setter
+ def quit_screen(self, quit_screen):
+ """Set the UIScreen based instance which will be showed before the Application will quit.
+
+ You can also use `simpleline.render.adv_widgets.YesNoDialog` or `UIScreen` based class
+ with the `answer` property. Without the `answer` property the application will always
+ close.
+ """
+ self._quit_screen = quit_screen
+
+ @property
+ def nothing_to_render(self):
+ """Is something for rendering in the scheduler stack?
+
+ :return: True if the rendering stack is empty
+ :rtype: bool
+ """
+ return self._screen_stack.empty()
+
+ def dump_stack(self):
+ """Get string representation of actual screen stack."""
+ return self._screen_stack.dump_stack()
+
+ def schedule_screen(self, ui_screen, args=None):
+ """Add screen to the bottom of the stack.
+
+ This is mostly useful at the beginning to prepare the first screen hierarchy to display.
+
+ :param ui_screen: screen to show
+ :type ui_screen: UIScreen instance
+ :param args: optional argument, please see switch_screen for details
+ :type args: anything
+ """
+ log.debug("Scheduling screen %s", ui_screen)
+ screen = ScreenData(ui_screen, args)
+ self._screen_stack.add_first(screen)
+ self._redraw_on_first_scheduled_screen()
+
+ def _redraw_on_first_scheduled_screen(self):
+ if not self._first_screen_scheduled:
+ self.redraw()
+ self._first_screen_scheduled = True
+
+ def replace_screen(self, ui_screen, args=None):
+ """Schedules a screen to replace the current one.
+
+ :param ui_screen: screen to show
+ :type ui_screen: instance of UIScreen
+ :param args: optional argument to pass to ui's refresh and setup methods
+ (can be used to select what item should be displayed or so)
+ :type args: anything
+ """
+ log.debug("Replacing screen %s", ui_screen)
+ try:
+ execute_new_loop = self._screen_stack.pop().execute_new_loop
+ except ScreenStackEmptyException as e:
+ raise ScreenStackEmptyException("Switch screen is not possible when there is no "
+ "screen scheduled!") from e
+
+ # we have to keep the old_loop value so we stop
+ # dialog's mainloop if it ever uses switch_screen
+ screen = ScreenData(ui_screen, args, execute_new_loop)
+ self._screen_stack.append(screen)
+ self.redraw()
+
+ def push_screen(self, ui_screen, args=None):
+ """Schedules a screen to show, but keeps the current one in stack to
+ return to, when the new one is closed.
+
+ :param ui_screen: screen to show
+ :type ui_screen: UIScreen instance
+ :param args: optional argument
+ :type args: anything
+ """
+ log.debug("Pushing screen %s to stack", ui_screen)
+ screen = ScreenData(ui_screen, args, False)
+ self._screen_stack.append(screen)
+ self.redraw()
+
+ def push_screen_modal(self, ui_screen, args=None):
+ """Starts a new screen right away, so the caller can collect data back.
+
+ When the new screen is closed, the caller is redisplayed.
+
+ This method does not return until the new screen is closed.
+
+ :param ui_screen: screen to show
+ :type ui_screen: UIScreen instance
+ :param args: optional argument, please see switch_screen for details
+ :type args: anything
+ """
+ log.debug("Pushing modal screen %s to stack", ui_screen)
+ screen = ScreenData(ui_screen, args, True)
+ self._screen_stack.append(screen)
+ # only new events will be processed now
+ # the old one will wait after this event loop will be closed
+ self._event_loop.execute_new_loop(RenderScreenSignal(self))
+
+ def _close_screen_callback(self, signal, data):
+ self.close_screen(signal.source)
+
+ def close_screen(self, closed_from=None):
+ """Close the currently displayed screen and exit it's main loop if necessary.
+
+ Next screen from the stack is then displayed.
+ """
+ screen = self._screen_stack.pop()
+ log.debug("Closing screen %s from %s", screen, closed_from)
+
+ # User can react when screen is closing
+ screen.ui_screen.closed()
+
+ if closed_from is not None and closed_from is not screen.ui_screen:
+ raise RenderUnexpectedError("You are trying to close screen %s from screen %s! "
+ "This is most probably not intentional." %
+ (closed_from, screen.ui_screen))
+
+ if screen.execute_new_loop:
+ self._event_loop.close_loop()
+
+ # redraw screen if there is what to redraw
+ # and if it is not modal screen (modal screen parent is blocked)
+ if not self._screen_stack.empty() and not screen.execute_new_loop:
+ self.redraw()
+
+ # we can't draw anything more. Kill the application.
+ if self._screen_stack.empty():
+ raise ExitMainLoop()
+
+ def redraw(self):
+ """Register rendering to the event loop for processing."""
+ self._event_loop.enqueue_signal(RenderScreenSignal(self))
+
+ def _process_screen_callback(self, signal, data):
+ self._process_screen()
+
+ def _process_screen(self):
+ """Process the current screen.
+
+ 1) It will call setup if the screen is not already set.
+ 2a) If setup was success then draw the screen.
+ 2b) If setup wasn't successful then pop the screen and try to process next in the stack.
+ Continue by (1).
+ 3)Ask for user input if requested.
+ """
+ top_screen = self._get_last_screen()
+
+ log.debug("Processing screen %s", top_screen)
+
+ # this screen is used first time (call setup() method)
+ if not top_screen.ui_screen.screen_ready:
+ if not top_screen.ui_screen.setup(top_screen.args):
+ # remove the screen and skip if setup went wrong
+ self._screen_stack.pop()
+ self.redraw()
+ log.warning("Screen %s setup wasn't successful", top_screen)
+ return
+
+ # get the widget tree from the screen and show it in the screen
+ try:
+ # refresh screen content
+ top_screen.ui_screen.refresh(top_screen.args)
+
+ # Screen was closed in the refresh method
+ if top_screen != self._get_last_screen():
+ return
+
+ # draw screen to the console
+ self._draw_screen(top_screen)
+
+ if top_screen.ui_screen.input_required:
+ log.debug("Input is required by %s screen", top_screen)
+ top_screen.ui_screen.get_input_with_error_check(top_screen.args)
+ except ExitMainLoop: # pylint: disable=try-except-raise
+ raise
+ except Exception: # pylint: disable=broad-except
+ self._event_loop.enqueue_signal(ExceptionSignal(self))
+ return
+
+ def _draw_screen(self, active_screen):
+ """Draws the current `active_screen`.
+
+ :param active_screen: Screen which should be draw to the console.
+ :type active_screen: Classed based on `simpleline.render.screen.UIScreen`.
+ """
+ # get the widget tree from the screen and show it in the screen
+ try:
+ if not active_screen.ui_screen.no_separator:
+ # separate the content on the screen from the stuff we are about to display now
+ print(self._spacer())
+
+ # print UIScreen content
+ active_screen.ui_screen.show_all()
+ except ExitMainLoop: # pylint: disable=try-except-raise
+ raise
+ except Exception: # pylint: disable=broad-except
+ self._event_loop.enqueue_signal(ExceptionSignal(self))
+
+ def _get_last_screen(self):
+ if self._screen_stack.empty():
+ raise ExitMainLoop()
+
+ return self._screen_stack.pop(False)
+
+ def process_input_result(self, input_result, should_redraw):
+ active_screen = self._get_last_screen()
+
+ if not input_result.was_successful():
+ if should_redraw:
+ self.redraw()
+ else:
+ log.debug("Input was not successful, ask for new input.")
+ active_screen.ui_screen.get_input_with_error_check(active_screen.args)
+ else:
+ if input_result == UserInputAction.NOOP:
+ return
+
+ if input_result == UserInputAction.REDRAW:
+ self.redraw()
+ elif input_result == UserInputAction.CLOSE:
+ self.close_screen()
+ elif input_result == UserInputAction.QUIT:
+ if self.quit_screen:
+ self.push_screen_modal(self.quit_screen)
+ try:
+ if self.quit_screen.answer is True:
+ raise ExitMainLoop()
+
+ self.redraw()
+ except AttributeError as e:
+ raise ExitMainLoop() from e
+ else:
+ raise ExitMainLoop()
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/render/screen_stack.py b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/render/screen_stack.py
new file mode 100644
index 0000000..9d0d7c2
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/render/screen_stack.py
@@ -0,0 +1,117 @@
+# Classes implementation for storing and manipulating Screen stack.
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+# Author(s): Jiri Konecny
+#
+
+
+from simpleline.errors import SimplelineError
+
+
+class ScreenStackException(SimplelineError):
+ """General screen stack exception."""
+
+
+class ScreenStackEmptyException(ScreenStackException):
+ """Screen stack exception when stack is empty."""
+
+
+class ScreenStack():
+ """Managing screen stack used in `ScreenScheduler`."""
+
+ def __init__(self):
+ self._screens = []
+
+ def empty(self):
+ """Test if screen stack is empty.
+
+ :return: True if empty.
+ :rtype: bool
+ """
+ return not self._screens
+
+ def size(self):
+ """Get size of the stack.
+
+ :return: Size of the stack.
+ """
+ return len(self._screens)
+
+ def append(self, screen):
+ """Add new screen to the top of the stack.
+
+ :param screen: Screen for the future rendering.
+ :type screen: Class based on `simpleline.render.ui_screen.UIScreen`.
+ """
+ self._screens.append(screen)
+
+ def pop(self, remove=True):
+ """Return top item from the stack.
+
+ :param remove: If True (default) also remove this items from the stack.
+ :return: The top screen on the stack.
+ """
+ try:
+ if remove:
+ return self._screens.pop()
+
+ return self._screens[-1]
+ except IndexError as e:
+ raise ScreenStackEmptyException(e) from e
+
+ def add_first(self, screen):
+ """Add `screen` to the bottom of the stack.
+
+ :param screen: Add the `screen` to the bottom of the stack.
+ :type screen: Class based on `simpleline.render.ui_screen.UIScreen`.
+ """
+ self._screens.insert(0, screen)
+
+ def dump_stack(self):
+ """Dump screen stack structure.
+
+ :returns: Screen stack representation.
+ :rtype: str
+ """
+ msg = '======= Screen stack =======\n'
+ msg += '----------- TOP ------------\n'
+
+ for screen in reversed(self._screens):
+ msg += str(screen)
+ msg += "\n"
+
+ msg += '============================\n'
+
+ return msg
+
+
+class ScreenData():
+ """Inner data class to store screen data."""
+
+ def __init__(self, ui_screen, args=None, execute_new_loop=False):
+ self.ui_screen = ui_screen
+ self.args = args
+ self.execute_new_loop = execute_new_loop
+
+ def __str__(self):
+ msg = self.__class__.__name__
+ msg += "("
+ msg += ",".join((str(self.ui_screen), str(self.args), str(self.execute_new_loop)))
+ msg += ")"
+ return msg
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/render/widgets.py b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/render/widgets.py
new file mode 100644
index 0000000..2f645a0
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/render/widgets.py
@@ -0,0 +1,495 @@
+# Widgets for Text UI framework.
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+
+
+import functools
+from textwrap import wrap
+from simpleline.utils.i18n import _
+from simpleline.utils import ensure_str
+
+__all__ = ["Widget", "TextWidget", "SeparatorWidget", "EntryWidget", "ColumnWidget",
+ "CheckboxWidget", "CenterWidget"]
+
+
+class Widget():
+
+ def __init__(self, max_width=None, default=None):
+ """Initializes base Widgets buffer.
+
+ This class can be subclassed to create customized widgets.
+
+ :param max_width: serves as a hint about screen size to write method with default arguments
+ :type max_width: int
+
+ :param default: string containing the default content to fill the buffer with
+ :type default: string
+ """
+ self._buffer = []
+ if default:
+ self._buffer = [[c for c in l] for l in default.split("\n")] # pylint: disable=unnecessary-comprehension
+ self._max_width = max_width
+ self._cursor = (0, 0) # row, col
+
+ @property
+ def height(self):
+ """The current height of the internal buffer."""
+ return len(self._buffer)
+
+ @property
+ def width(self):
+ """The current width of the internal buffer (id of the first empty column)."""
+ return functools.reduce(lambda acc, l: max(acc, len(l)), self._buffer, 0)
+
+ def clear(self):
+ """Clears this widgets buffer and resets cursor."""
+ self._buffer = list()
+ self._cursor = (0, 0)
+
+ @property
+ def content(self):
+ """Return a list (rows) of lists (columns) with one character elements."""
+ return self._buffer
+
+ def render(self, width):
+ """Redraw the widget's self._buffer.
+
+ :param width: the width of buffer requested by the caller
+ :type width: int
+
+ Commonly, call render of child widgets and then draw and write
+ methods to copy their contents to self._buffer.
+ """
+ self.clear()
+
+ def get_lines(self):
+ """Return lines to write out in order to show this widget.
+
+ :return: lines representing this widget
+ :rtype: list(str)
+ """
+ return [str(u"".join(line)) for line in self._buffer]
+
+ def set_cursor_position(self, row, col):
+ """Set cursor position.
+
+ :param row: row id, starts with 0 at the top of the screen
+ :type row: int
+
+ :param col: column id, starts with 0 on the left side of the screen
+ :type col: int
+ """
+ self._cursor = (row, col)
+
+ @property
+ def cursor(self):
+ return self._cursor
+
+ def set_end(self):
+ """Set the cursor to first column in new line at the end."""
+ self._cursor = (self.height, 0)
+
+ def draw(self, w, row=None, col=None, block=False):
+ """Copy w widget's content to this widget's buffer at row, col position.
+
+ :param w: widget to take content from
+ :type w: class Widget
+
+ :param row: row number to start at (default is at the cursor position)
+ :type row: int
+
+ :param col: column number to start at (default is at the cursor position)
+ :type col: int
+
+ :param block: when printing newline, start at column col (True) or at column 0 (False)
+ :type block: boolean
+ """
+ # if the starting row is not present, start at the cursor position
+ if row is None:
+ row = self._cursor[0]
+
+ # if the starting column is not present, start at the cursor position
+ if col is None:
+ col = self._cursor[1]
+
+ # fill up rows to accommodate for w.height
+ if self.height < row + w.height:
+ for _i in range(row + w.height - self.height):
+ self._buffer.append(list())
+
+ # append columns to accommodate for w.width
+ for l in range(row, row + w.height):
+ l_len = len(self._buffer[l])
+ w_len = len(w.content[l - row])
+ if l_len < col + w_len:
+ self._buffer[l] += ((col + w_len - l_len) * list(u" "))
+ self._buffer[l][col:col + w_len] = w.content[l - row][:]
+
+ # move the cursor to new spot
+ if block:
+ self._cursor = (row + w.height, col)
+ else:
+ self._cursor = (row + w.height, 0)
+
+ def write(self, text, row=None, col=None, width=None, block=False, wordwrap=False):
+ """Emulate the typing machine writing to this widget's buffer.
+
+ :param text: text to type
+ :type text: str
+
+ :param row: row number to start at (default is at the cursor position)
+ :type row: int
+
+ :param col: column number to start at (default is at the cursor position)
+ :type col: int
+
+ :param width: wrap at "col" + "width" column (default is at self._max_width)
+ :type width: int
+
+ :param block: when printing newline, start at column col (True) or at column 0 (False)
+ :type block: boolean
+
+ :param wordwrap: wrap by words
+ :type wordwrap: boolean
+ """
+ if not text:
+ return
+
+ text = ensure_str(text)
+ if row is None:
+ row = self._cursor[0]
+
+ if col is None:
+ col = self._cursor[1]
+
+ if width is None and self._max_width:
+ width = self._max_width - col
+
+ x = row
+ y = col
+
+ if wordwrap:
+ text = self._wrap_words(text, width)
+
+ # emulate typing machine
+ for character in text:
+ # FIXME: Remove the code duplication below and optimize it
+ # process newline
+ if character == "\n":
+ x += 1
+ if block:
+ y = col
+ else:
+ y = 0
+
+ self._increase_x_buffer_size(x)
+ continue
+
+ self._increase_x_buffer_size(x)
+ self._increase_y_buffer_size(x, y)
+
+ self._save_character_to_buffer(x, y, character)
+
+ # shift to the next char
+ y += 1
+ if width is not None and y >= col + width:
+ x += 1
+ if block:
+ y = col
+ else:
+ y = 0
+
+ self._cursor = (x, y)
+
+ def _increase_x_buffer_size(self, x):
+ if x >= len(self._buffer):
+ for _i in range(x - len(self._buffer) + 1):
+ self._buffer.append(list())
+
+ def _increase_y_buffer_size(self, x, y):
+ if y >= len(self._buffer[x]):
+ self._buffer[x] += ((y - len(self._buffer[x]) + 1) * list(u" "))
+
+ def _save_character_to_buffer(self, x, y, character):
+ self._buffer[x][y] = character
+
+ @staticmethod
+ def _wrap_words(text, width):
+ lines = []
+ # Wrap each line separately
+ for line in text.split('\n'):
+ sublines = []
+ for subline in wrap(line, width):
+ sublines.append(subline)
+ if len(subline) < width:
+ # line shorter than width will be wrapped by '\n' we add
+ sublines.append('\n')
+ # line with length == width will be wrapped by the width based
+ # wrapping logic
+ # end of line will be wrapped by '\n' following the line in
+ # original text
+ if sublines and sublines[-1] == '\n':
+ sublines.pop()
+ lines.append("".join(sublines))
+ return '\n'.join(lines)
+
+
+class TextWidget(Widget):
+ """Class to handle wrapped text output."""
+
+ def __init__(self, text):
+ """
+ :param text: text to format
+ :type text: str
+ """
+ super().__init__()
+ self._text = text
+
+ @property
+ def text(self):
+ """Contains text of this widget."""
+ return self._text
+
+ def render(self, width):
+ """Renders the text widget limited to width number of columns.
+
+ Wraps to the next line when the text is longer.
+
+ :param width: maximum width allocated to the string
+ :type width: int
+ """
+ super().render(width)
+ self.write(self._text, width=width, wordwrap=True)
+
+
+class EntryWidget(TextWidget):
+ """This is the easy way how to generate entry items for containers.
+
+ If the numbering in a container is turned on the output looks like:
+
+ N) title
+ value
+
+ Without numbering turned on:
+
+ title
+ value
+ """
+
+ def __init__(self, title, value=None):
+ """ Create Entry widget instance.
+
+ :param title: Title of this entry.
+ :type title: String.
+
+ :param value: Actual value printed in second line below the title.
+ :type value: String.
+ """
+ text = self._create_text(title=title, value=value)
+ super().__init__(text)
+
+ @staticmethod
+ def _create_text(title, value):
+ msg = title
+
+ if value:
+ msg += "\n"
+ msg += value
+
+ return msg
+
+
+class SeparatorWidget(Widget):
+ """Print empty line."""
+
+ def __init__(self, lines=1):
+ """Construct SeparatorWidget for printing blank lines.
+
+ :param lines: How many lines should be blank.
+ :type lines: int greater than 0.
+ """
+ super().__init__()
+ self._lines = lines
+
+ def render(self, width):
+ """Render empty line to the buffer.
+
+ :param width: maximum width allocated to the string
+ :type width: int
+ """
+ super().render(width)
+ self.write("")
+
+ def write(self, text, row=None, col=None, width=None, block=False, wordwrap=False):
+ """Optimize write function.
+
+ To print just a blank line we don't need too much logic.
+ """
+ for i in range(0, self._lines):
+ self._buffer.append(list())
+ self._buffer[i] += u""
+ self.set_cursor_position(self._lines - 1, 0)
+
+
+class CenterWidget(Widget):
+ """Class to handle horizontal centering of content."""
+
+ def __init__(self, w):
+ """
+ :param w: widget to center
+ :type w: Widget
+ """
+ super().__init__()
+ self._w = w
+
+ def render(self, width):
+ """Render the centered widget to internal buffer.
+
+ :param width: maximum width the widget should use
+ :type width: int
+ """
+ super().render(width)
+ self._w.render(width)
+ # make sure col is an integer
+ self.draw(self._w, col=(width - self._w.width) // 2)
+
+
+class CheckboxWidget(Widget):
+ """Widget to show checkbox with (un)checked box, name and description."""
+
+ def __init__(self, key="x", title=None, text=None, completed=None):
+ """
+ :param key: tick character to be used inside [ ]
+ :type key: character
+
+ :param title: the title next to the [ ] box
+ :type title: str
+
+ :param text: the description text to be shown on the second row in ()
+ :type text: str
+
+ :param completed: is the checkbox ticked or not?
+ :type completed: True|False
+ """
+ super().__init__()
+ self._key = key
+ self._title = title
+ self._text = text
+ self._completed = completed
+
+ def render(self, width):
+ """Render the widget to internal buffer.
+
+ It should be max width characters wide.
+ """
+ super().render(width)
+
+ if self.completed:
+ checkchar = self._key
+ else:
+ checkchar = " "
+
+ # prepare the checkbox
+ checkbox = TextWidget("[%s]" % checkchar)
+
+ data = []
+
+ # append lines
+ if self.title:
+ data.append(TextWidget(_(self.title)))
+
+ if self.text:
+ data.append(TextWidget("(%s)" % self.text))
+
+ # the checkbox has two columns
+ # [x] is one and is 3 chars wide
+ # text is second and can occupy width - 3 - 1 (for space) chars
+ cols = ColumnWidget([(3, [checkbox]), (width - 4, data)], 1)
+ cols.render(width)
+
+ # transfer the column widget rendered stuff to internal buffer
+ self.draw(cols)
+
+ @property
+ def title(self):
+ """Returns the first line (main title) of the checkbox."""
+ return self._title
+
+ @property
+ def completed(self):
+ """Returns the state of the checkbox, checked is True."""
+ return self._completed
+
+ @property
+ def text(self):
+ """Contains the description text from the second line."""
+ return self._text
+
+
+class ColumnWidget(Widget):
+
+ def __init__(self, columns, spacing=0):
+ """Create text columns
+
+ Deprecated. Please do not use this widget, use containers instead.
+
+ :param columns: list containing (column width, [list of widgets to put into this column])
+ :type columns: [(int, [...]), ...]
+
+ :param spacing: number of spaces to use between columns
+ :type spacing: int
+ """
+ super().__init__()
+ self._spacing = spacing
+ self._columns = columns
+
+ def render(self, width):
+ """Render the widget to it's internal buffer
+
+ :param width: the maximum width the widget can use
+ :type width: int
+
+ :return: nothing
+ """
+ super().render(width)
+
+ # the leftmost empty column
+ col_pos = 0
+
+ # iterate over tuples (column width, column content)
+ for col_width, col in self._columns:
+
+ # set cursor to first line and leftmost empty column
+ self.set_cursor_position(0, col_pos)
+
+ # if requested width is None, limit the maximum to width
+ # and set minimum to 0
+ if col_width is None:
+ col_max_width = width - self.cursor[1]
+ col_width = 0
+ else:
+ col_max_width = col_width
+
+ # render and draw contents of column
+ for item in col:
+ item.render(col_max_width)
+ self.draw(item, block=True)
+
+ # recompute the leftmost empty column
+ col_pos = max((col_pos + col_width), self.width) + self._spacing
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/utils/__init__.py b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/utils/__init__.py
new file mode 100644
index 0000000..bb1622c
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/utils/__init__.py
@@ -0,0 +1,94 @@
+# iutil.py - generic install utility functions
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+
+import sys
+import string # pylint: disable=deprecated-module
+import unicodedata
+
+
+def ensure_str(str_or_bytes, keep_none=True):
+ """
+ Returns a str instance for given string or ``None`` if requested to keep it.
+
+ :param str_or_bytes: string to be kept or converted to str type
+ :type str_or_bytes: str or bytes
+ :param bool keep_none: whether to keep None as it is or raise ValueError if
+ ``None`` is passed
+ :raises ValueError: if applied on an object not being of type bytes nor str
+ (nor NoneType if ``keep_none`` is ``False``)
+ """
+ if keep_none and str_or_bytes is None:
+ return None
+ if isinstance(str_or_bytes, str):
+ return str_or_bytes
+ if isinstance(str_or_bytes, bytes):
+ return str_or_bytes.decode(sys.getdefaultencoding())
+
+ raise ValueError(
+ "str_or_bytes must be of type 'str' or 'bytes', not '%s'" % type(str_or_bytes))
+
+
+# Define translations between ASCII uppercase and lowercase for
+# locale-independent string conversions. The tables are 256-byte string used
+# with str.translate. If str.translate is used with a unicode string,
+# even if the string contains only 7-bit characters, str.translate will
+# raise a UnicodeDecodeError.
+_ASCIIlower_table = str.maketrans(string.ascii_uppercase, string.ascii_lowercase)
+_ASCIIupper_table = str.maketrans(string.ascii_lowercase, string.ascii_uppercase)
+
+
+def _toASCII(s):
+ """Convert a unicode string to ASCII"""
+ if isinstance(s, str):
+ # Decompose the string using the NFK decomposition, which in addition
+ # to the canonical decomposition replaces characters based on
+ # compatibility equivalence (e.g., ROMAN NUMERAL ONE has its own code
+ # point but it's really just a capital I), so that we can keep as much
+ # of the ASCII part of the string as possible.
+ s = unicodedata.normalize('NFKD', s).encode('ascii', 'ignore').decode("ascii")
+ elif not isinstance(s, bytes):
+ s = ''
+ return s
+
+
+def lowerASCII(s):
+ """Convert a string to lowercase using only ASCII character definitions.
+
+ The returned string will contain only ASCII characters. This function is
+ locale-independent.
+ """
+ # XXX: Python 3 has str.maketrans() and bytes.maketrans() so we should
+ # ideally use one or the other depending on the type of 's'. But it turns
+ # out we expect this function to always return string even if given bytes.
+ s = ensure_str(s)
+ return str.translate(_toASCII(s), _ASCIIlower_table)
+
+
+def upperASCII(s):
+ """Convert a string to uppercase using only ASCII character definitions.
+
+ The returned string will contain only ASCII characters. This function is
+ locale-independent.
+ """
+ # XXX: Python 3 has str.maketrans() and bytes.maketrans() so we should
+ # ideally use one or the other depending on the type of 's'. But it turns
+ # out we expect this function to always return string even if given bytes.
+ s = ensure_str(s)
+ return str.translate(_toASCII(s), _ASCIIupper_table)
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/utils/i18n.py b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/utils/i18n.py
new file mode 100644
index 0000000..c740c34
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/simpleline/utils/i18n.py
@@ -0,0 +1,61 @@
+# Translation functions we use all over the place
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+
+__all__ = ["_", "N_", "P_", "C_", "CN_", "CP_"]
+
+import gettext
+
+N_ = lambda x: x
+_ = lambda x: gettext.translation("python-simpleline", fallback=True).gettext(x) if x != "" else ""
+P_ = lambda x, y, z: gettext.translation("python-simpleline", fallback=True).ngettext(x, y, z)
+
+# This is equivalent to "pgettext" in GNU gettext. The pgettext functions
+# are not exported by Python, but all they really do is a stick a EOT
+# character between msgctxt and msgid and check that msgctxt isn't part
+# of the return value.
+
+
+def C_(msgctxt, msgid):
+ ctxid = "%s\x04%s" % (msgctxt, msgid)
+ translation = _(ctxid)
+
+ # If there is no translation for msgctxtmsgid, return only msgid
+ if translation == ctxid:
+ return msgid
+
+ return translation
+
+# Mark as translatable with context
+CN_ = lambda c, x: x
+
+# npgettext; i.e., gettext with plural form and context
+
+
+def CP_(msgctxt, msgid, msgid_plural, n):
+ ctxid = "%s\x04%s" % (msgctxt, msgid)
+ translation = P_(ctxid, msgid_plural, n)
+
+ # If the returned value is msgctxtmsgid, ngettext was trying to
+ # fallback to msgid. We don't add msgctxt to msgid_plural, so any other
+ # return value is correct.
+ if translation == ctxid:
+ return msgid
+
+ return translation
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/tests/units/__init__.py b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/tests/units/__init__.py
new file mode 100644
index 0000000..fb94082
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/tests/units/__init__.py
@@ -0,0 +1,45 @@
+# Helper functions for the test classes.
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+
+from simpleline import App
+from simpleline.global_configuration import DEFAULT_WIDTH
+
+
+class UtilityMixin():
+
+ @staticmethod
+ def calculate_separator(width=DEFAULT_WIDTH):
+ separator = "\n".join(2 * [width * "="])
+ separator += "\n" # print adds another newline
+ return separator
+
+ def create_output_with_separators(self, screens_text):
+ msg = ""
+ for screen_txt in screens_text:
+ msg += self.calculate_separator()
+ msg += screen_txt + "\n\n"
+
+ return msg
+
+ @staticmethod
+ def schedule_screen_and_run(screen):
+ App.initialize()
+ App.get_scheduler().schedule_screen(screen)
+ App.run()
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/tests/units/glib/__init__.py b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/tests/units/glib/__init__.py
new file mode 100644
index 0000000..7574516
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/tests/units/glib/__init__.py
@@ -0,0 +1,67 @@
+# Helper functions for the GLib test classes.
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+import gi
+
+from simpleline import App
+from simpleline.event_loop.glib_event_loop import GLibEventLoop
+
+gi.require_version("GLib", "2.0")
+
+from gi.repository import GLib # pylint: disable=wrong-import-position, wrong-import-order
+
+
+class GLibUtilityMixin():
+
+ def __init__(self):
+ self.loop = None
+ self.timeout_error = False
+
+ def _quit_loop(self, loop):
+ """Kill GLib loop."""
+ loop.quit()
+ self.timeout_error = True
+ return True
+
+ def create_glib_loop(self):
+ # clear flags
+ self.timeout_error = False
+ self.loop = GLibEventLoop()
+
+ loop = self.loop.active_main_loop
+ context = loop.get_context()
+
+ # This is prevention from running loop indefinitely
+ source = GLib.timeout_source_new_seconds(2)
+ source.set_callback(self._quit_loop, loop)
+ source.attach(context)
+
+ def setup_glib(self):
+ self.create_glib_loop()
+ App.initialize(event_loop=self.loop)
+
+ def teardown_glib(self):
+ if self.timeout_error:
+ raise AssertionError("Loop was killed by timeout!")
+
+ def schedule_screen_and_run_with_glib(self, screen):
+ self.setup_glib()
+
+ App.get_scheduler().schedule_screen(screen)
+ App.run()
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/tests/units/glib/event_loop_glib_test.py b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/tests/units/glib/event_loop_glib_test.py
new file mode 100644
index 0000000..fa3c257
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/tests/units/glib/event_loop_glib_test.py
@@ -0,0 +1,37 @@
+# Event loop test classes for GLib implementation.
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+
+from . import GLibUtilityMixin
+from ..main.event_loop_test import ProcessEvents_TestCase
+
+
+class GLibProcessEvents_TestCase(ProcessEvents_TestCase, GLibUtilityMixin):
+ """Run all the tests in ProcessEvents test case but with GLib event loop."""
+
+ def tearDown(self):
+ super().tearDown()
+ self.teardown_glib()
+
+ def create_loop(self):
+ self.create_glib_loop()
+
+
+# Hack to avoid running the original class thanks to import
+del ProcessEvents_TestCase
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/tests/units/glib/input_handler_glib_test.py b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/tests/units/glib/input_handler_glib_test.py
new file mode 100644
index 0000000..c1ff51b
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/tests/units/glib/input_handler_glib_test.py
@@ -0,0 +1,38 @@
+# Rendering screen test classes.
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+
+
+from . import GLibUtilityMixin
+from ..main.input_handler_test import InputHandler_TestCase
+
+
+class GLibInputHandler_TestCase(InputHandler_TestCase, GLibUtilityMixin):
+ """Run all the tests in InputHandler test case but with GLib event loop."""
+
+ def tearDown(self):
+ super().tearDown()
+ self.teardown_glib()
+
+ def create_loop(self):
+ self.create_glib_loop()
+
+
+# Hack to avoid running the original class thanks to import
+del InputHandler_TestCase
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/tests/units/glib/render_screen_glib_test.py b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/tests/units/glib/render_screen_glib_test.py
new file mode 100644
index 0000000..9a42b65
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/tests/units/glib/render_screen_glib_test.py
@@ -0,0 +1,73 @@
+# Rendering screen test classes for GLib implementation.
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+
+
+from . import GLibUtilityMixin
+from ..main.render_screen_test import SimpleUIScreenProcessing_TestCase, \
+ InputProcessing_TestCase, ScreenException_TestCase
+
+
+class GLibSimpleUIScreenProcessing_TestCase(SimpleUIScreenProcessing_TestCase, GLibUtilityMixin):
+ """Run all the tests in ProcessEvents test case but with GLib event loop."""
+
+ def setUp(self):
+ super().setUp()
+ self.loop = None
+ self.timeout_error = False
+
+ def tearDown(self):
+ super().tearDown()
+ self.teardown_glib()
+
+ def schedule_screen_and_run(self, screen):
+ self.schedule_screen_and_run_with_glib(screen)
+
+
+# Hack to avoid running the original class thanks to import
+del SimpleUIScreenProcessing_TestCase
+
+
+class GLibScreenException_TestCase(ScreenException_TestCase, GLibUtilityMixin):
+
+ def tearDown(self):
+ super().tearDown()
+ self.teardown_glib()
+
+ def schedule_screen_and_run(self, screen):
+ self.schedule_screen_and_run_with_glib(screen)
+
+
+# Hack to avoid running the original class thanks to import
+del ScreenException_TestCase
+
+
+class GLibInputProcessing_TestCase(InputProcessing_TestCase, GLibUtilityMixin):
+
+ def setUp(self):
+ super().setUp()
+ self.setup_glib()
+
+ def tearDown(self):
+ super().tearDown()
+ self.teardown_glib()
+
+
+# Hack to avoid running the original class thanks to import
+del InputProcessing_TestCase
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/tests/units/glib/screen_scheduler_glib_test.py b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/tests/units/glib/screen_scheduler_glib_test.py
new file mode 100644
index 0000000..93148a1
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/tests/units/glib/screen_scheduler_glib_test.py
@@ -0,0 +1,37 @@
+# Screen scheduling GLib test classes.
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+
+
+from . import GLibUtilityMixin
+from ..main.screen_scheduler_test import ScreenScheduler_TestCase
+
+
+class GLibScreenScheduler_TestCase(ScreenScheduler_TestCase, GLibUtilityMixin):
+
+ def tearDown(self):
+ super().tearDown()
+ self.teardown_glib()
+
+ def schedule_screen_and_run(self, screen):
+ self.schedule_screen_and_run_with_glib(screen)
+
+
+# Hack to avoid running the original class thanks to import
+del ScreenScheduler_TestCase
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/tests/units/main/__init__.py b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/tests/units/main/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/tests/units/main/adv_widgets_test.py b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/tests/units/main/adv_widgets_test.py
new file mode 100644
index 0000000..03048bf
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/tests/units/main/adv_widgets_test.py
@@ -0,0 +1,118 @@
+# Advanced widgets test cases.
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+
+import unittest
+
+from unittest.mock import patch
+from io import StringIO
+
+from simpleline.render.adv_widgets import GetInputScreen, GetPasswordInputScreen
+
+from .. import UtilityMixin
+
+
+@patch('simpleline.input.input_handler.InputHandlerRequest._get_input')
+@patch('sys.stdout', new_callable=StringIO)
+class AdvWidgets_TestCase(unittest.TestCase, UtilityMixin):
+ def setUp(self):
+ self.correct_input = False
+ self.args_used = False
+
+ def test_gettext(self, stdout_mock, stdin_mock):
+ prompt = "Type input"
+ input_text = "user input"
+ screen = GetInputScreen(message=prompt)
+ stdin_mock.return_value = input_text
+
+ self.schedule_screen_and_run(screen)
+
+ expected_output = self.create_output_with_separators(["%s: " % prompt]).rstrip('\n')
+
+ self.assertEqual(expected_output, stdout_mock.getvalue())
+ self.assertEqual(screen.value, input_text)
+
+ def test_gettext_with_condition(self, stdout_mock, stdin_mock):
+ prompt = "Type input"
+ wrong_input = "wrong"
+ condition = lambda x, _: x != wrong_input
+ stdin_mock.side_effect = self.input_generator()
+
+ screen = GetInputScreen(message=prompt)
+ screen.add_acceptance_condition(condition)
+
+ self.schedule_screen_and_run(screen)
+
+ temp_prompt = ["%s: %s: " % (prompt, prompt)]
+ expected_output = self.create_output_with_separators(temp_prompt).rstrip("\n")
+
+ self.assertEqual(expected_output, stdout_mock.getvalue())
+ self.assertTrue(self.correct_input)
+
+ def test_gettext_with_condition_and_use_arg(self, stdout_mock, stdin_mock):
+ prompt = "Type input"
+ user_input = "y"
+ stdin_mock.return_value = user_input
+
+ screen = GetInputScreen(message=prompt)
+ screen.add_acceptance_condition(self.acceptance_condition_test, "y")
+
+ self.schedule_screen_and_run(screen)
+
+ expected_msg = "%s: " % prompt
+ expected_output = self.create_output_with_separators([expected_msg]).rstrip("\n")
+
+ self.assertEqual(expected_output, stdout_mock.getvalue())
+ self.assertTrue(self.args_used)
+
+ @patch("simpleline.global_configuration.GlobalConfiguration.password_function")
+ def test_getpass(self, hiden_stdin_mock, stdout_mock, stdin_mock):
+ prompt = "Type input"
+ input_text = "user input"
+ screen = GetPasswordInputScreen(message=prompt)
+ hiden_stdin_mock.return_value = input_text
+
+ self.schedule_screen_and_run(screen)
+
+ self.assertEqual(screen.value, input_text)
+
+ @patch("simpleline.global_configuration.GlobalConfiguration.password_function")
+ def test_getpass_with_condition(self, hiden_stdin_mock, stdout_mock, stdin_mock):
+ prompt = "Type input"
+ wrong_input = "wrong"
+ condition = lambda x, _: x != wrong_input
+ hiden_stdin_mock.side_effect = self.input_generator()
+
+ screen = GetPasswordInputScreen(message=prompt)
+ screen.add_acceptance_condition(condition)
+
+ self.schedule_screen_and_run(screen)
+
+ self.assertTrue(self.correct_input)
+
+ def input_generator(self):
+ for i in ("wrong", "correct"):
+ if i == "correct":
+ self.correct_input = True
+ yield i
+
+ def acceptance_condition_test(self, user_input, args):
+ if user_input == args:
+ self.args_used = True
+ return True
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/tests/units/main/app_test.py b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/tests/units/main/app_test.py
new file mode 100644
index 0000000..02a5ee6
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/tests/units/main/app_test.py
@@ -0,0 +1,120 @@
+# App class test classes.
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+
+import unittest
+
+from unittest import mock
+
+from simpleline import App
+from simpleline.global_configuration import GlobalConfiguration
+from simpleline.input.input_threading import InputThreadManager
+from simpleline.render.screen_scheduler import ScreenScheduler
+from simpleline.event_loop.main_loop import MainLoop
+from simpleline.errors import NothingScheduledError
+
+
+class App_TestCase(unittest.TestCase):
+
+ def test_create_instance(self):
+ App.initialize()
+ self.assertTrue(isinstance(App.get_scheduler(), ScreenScheduler))
+ self.assertTrue(isinstance(App.get_event_loop(), MainLoop))
+ self.assertTrue(isinstance(App.get_configuration(), GlobalConfiguration))
+
+ def test_create_instance_with_custom_scheduler(self):
+ App.initialize(scheduler=CustomScreenScheduler(CustomEventLoop()))
+ self.assertTrue(isinstance(App.get_scheduler(), CustomScreenScheduler))
+
+ def test_create_instance_with_event_loop(self):
+ App.initialize(event_loop=CustomEventLoop())
+ self.assertTrue(isinstance(App.get_event_loop(), CustomEventLoop))
+
+ def test_create_instance_with_configuration(self):
+ App.initialize(global_configuration=CustomGlobalConfiguration())
+ self.assertTrue(isinstance(App.get_configuration(), CustomGlobalConfiguration))
+
+ def test_create_instance_with_custom_everything(self):
+ event_loop = CustomEventLoop()
+ App.initialize(event_loop=event_loop,
+ scheduler=CustomScreenScheduler(event_loop),
+ global_configuration=CustomGlobalConfiguration())
+
+ self.assertTrue(isinstance(App.get_event_loop(), CustomEventLoop))
+ self.assertTrue(isinstance(App.get_scheduler(), CustomScreenScheduler))
+ self.assertTrue(isinstance(App.get_configuration(), CustomGlobalConfiguration))
+
+ def test_reinitialize(self):
+ event_loop1 = CustomEventLoop()
+ event_loop2 = CustomEventLoop()
+ scheduler1 = CustomScreenScheduler(event_loop1)
+ scheduler2 = CustomScreenScheduler(event_loop2)
+ configuration1 = CustomGlobalConfiguration()
+ configuration2 = CustomGlobalConfiguration()
+
+ App.initialize(event_loop=event_loop1, scheduler=scheduler1,
+ global_configuration=configuration1)
+ self._check_app_settings(event_loop1, scheduler1, configuration1)
+
+ App.initialize(event_loop=event_loop2, scheduler=scheduler2,
+ global_configuration=configuration2)
+ self._check_app_settings(event_loop2, scheduler2, configuration2)
+
+ App.initialize()
+ self.assertNotEqual(App.get_event_loop(), event_loop2)
+ self.assertNotEqual(App.get_scheduler(), scheduler2)
+ self.assertNotEqual(App.get_configuration(), configuration2)
+
+ def test_input_thread_manager_after_initialize(self):
+ App.initialize()
+
+ thread_mgr = InputThreadManager.get_instance()
+
+ App.initialize()
+
+ self.assertNotEqual(thread_mgr, InputThreadManager.get_instance())
+
+ @mock.patch('simpleline.event_loop.main_loop.MainLoop.run')
+ def test_run_shortcut(self, run_mock):
+ App.initialize()
+ App.get_configuration().should_run_with_empty_stack = True
+ App.run()
+ self.assertTrue(run_mock.called)
+
+ def test_run_with_empty_screen_stack(self):
+ App.initialize()
+ with self.assertRaises(NothingScheduledError):
+ App.run()
+
+ def _check_app_settings(self, event_loop, scheduler, configuration):
+ self.assertEqual(App.get_event_loop(), event_loop)
+ self.assertEqual(App.get_scheduler(), scheduler)
+ self.assertEqual(App.get_configuration(), configuration)
+
+
+class CustomScreenScheduler(ScreenScheduler):
+ pass
+
+
+class CustomEventLoop(MainLoop):
+ pass
+
+
+class CustomGlobalConfiguration(GlobalConfiguration):
+ pass
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/tests/units/main/containers_test.py b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/tests/units/main/containers_test.py
new file mode 100644
index 0000000..0c7d2de
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/tests/units/main/containers_test.py
@@ -0,0 +1,394 @@
+# Containers test classes.
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+
+import unittest
+
+from io import StringIO
+from unittest.mock import patch
+
+from simpleline import App
+from simpleline.render.containers import WindowContainer, ListRowContainer, ListColumnContainer, \
+ KeyPattern
+from simpleline.render.screen import UIScreen, InputState
+from simpleline.render.widgets import TextWidget
+
+from .widgets_test import BaseWidgets_TestCase
+
+
+class Containers_TestCase(BaseWidgets_TestCase):
+
+ def _test_callback(self, data):
+ pass
+
+ def test_listrow_container(self):
+ c = ListRowContainer(columns=2,
+ items=[self.w2, self.w3, self.w5],
+ columns_width=10,
+ spacing=2,
+ numbering=False)
+ c.render(25)
+
+ expected_result = [u"Test Test 2",
+ u"Test 3"]
+ res_lines = c.get_lines()
+
+ self.evaluate_result(res_lines, expected_result)
+
+ def test_empty(self):
+ c = ListRowContainer(columns=1)
+
+ c.render(10)
+ result = c.get_lines()
+
+ self.assertEqual(len(result), 0)
+
+ def test_more_columns_than_widgets(self):
+ c = ListRowContainer(columns=3, items=[self.w1], columns_width=40, numbering=False)
+ c.render(80)
+
+ expected_result = [u"Můj krásný dlouhý text"]
+
+ res_lines = c.get_lines()
+ self.evaluate_result(res_lines, expected_result)
+
+ def test_listrow_wrapping(self):
+ # spacing is 3 by default
+ c = ListRowContainer(2,
+ [self.w1, self.w2, self.w3, self.w4],
+ columns_width=15,
+ numbering=False)
+ c.render(25)
+
+ expected_result = [u"Můj krásný Test",
+ u"dlouhý text",
+ u"Test 2 Krásný dlouhý",
+ u" text podruhé"]
+ res_lines = c.get_lines()
+ self.evaluate_result(res_lines, expected_result)
+
+ def test_newline_wrapping(self):
+ widgets = [TextWidget("Hello"), TextWidget("Wrap\nthis\ntext"), TextWidget("Hi"),
+ TextWidget("Hello2")]
+
+ c = ListRowContainer(3, widgets, columns_width=6, spacing=1, numbering=False)
+ c.render(80)
+
+ expected_result = [u"Hello Wrap Hi",
+ u" this",
+ u" text",
+ u"Hello2"]
+ res_lines = c.get_lines()
+ self.evaluate_result(res_lines, expected_result)
+
+ def test_listcolumn_container(self):
+ c = ListColumnContainer(columns=2,
+ items=[self.w2, self.w3, self.w5],
+ columns_width=10,
+ spacing=2,
+ numbering=False)
+ c.render(25)
+
+ expected_result = [u"Test Test 3",
+ u"Test 2"]
+ res_lines = c.get_lines()
+ self.evaluate_result(res_lines, expected_result)
+
+ def test_listcolumn_wrapping(self):
+ # spacing is 3 by default
+ c = ListColumnContainer(2,
+ [self.w1, self.w2, self.w3, self.w4],
+ columns_width=15,
+ numbering=False)
+ c.render(25)
+
+ expected_result = [u"Můj krásný Test 2",
+ u"dlouhý text",
+ u"Test Krásný dlouhý",
+ u" text podruhé"]
+ res_lines = c.get_lines()
+ self.evaluate_result(res_lines, expected_result)
+
+ def test_add_new_container(self):
+ c = ListRowContainer(columns=2,
+ items=[TextWidget("Ahoj")],
+ columns_width=15,
+ spacing=0,
+ numbering=False)
+
+ expected_result = [u"Ahoj"]
+
+ c.render(80)
+ self.evaluate_result(c.get_lines(), expected_result)
+
+ c.add(TextWidget("Nový widget"))
+ c.add(TextWidget("Hello"))
+
+ expected_result = [u"Ahoj Nový widget",
+ u"Hello"]
+
+ c.render(80)
+ self.evaluate_result(c.get_lines(), expected_result)
+
+ def test_column_numbering(self):
+ # spacing is 3 by default
+ c = ListColumnContainer(2, [self.w1, self.w2, self.w3, self.w4], columns_width=16)
+ c.render(25)
+
+ expected_result = [u"1) Můj krásný 3) Test 2",
+ u" dlouhý text",
+ u"2) Test 4) Krásný dlouhý",
+ u" text podruhé"]
+ res_lines = c.get_lines()
+ self.evaluate_result(res_lines, expected_result)
+
+ def test_row_numbering(self):
+ # spacing is 3 by default
+ c = ListRowContainer(2, [self.w1, self.w2, self.w3, self.w4], columns_width=16)
+ c.render(25)
+
+ expected_result = [u"1) Můj krásný 2) Test",
+ u" dlouhý text",
+ u"3) Test 2 4) Krásný dlouhý",
+ u" text podruhé"]
+ res_lines = c.get_lines()
+ self.evaluate_result(res_lines, expected_result)
+
+ def test_custom_numbering(self):
+ # spacing is 3 by default
+ c = ListRowContainer(2, [self.w1, self.w2, self.w3, self.w4], columns_width=20)
+ c.key_pattern = KeyPattern("a {:d} a ")
+ c.render(25)
+
+ expected_result = [u"a 1 a Můj krásný a 2 a Test",
+ u" dlouhý text",
+ u"a 3 a Test 2 a 4 a Krásný dlouhý",
+ u" text podruhé"]
+ res_lines = c.get_lines()
+ self.evaluate_result(res_lines, expected_result)
+
+ def test_window_container(self):
+ c = WindowContainer(title="Test")
+
+ c.add(TextWidget("Body"))
+ c.render(10)
+
+ expected_result = [u"Test",
+ u"",
+ u"Body"]
+
+ res_lines = c.get_lines()
+ self.evaluate_result(res_lines, expected_result)
+
+ def test_window_container_with_multiple_items(self):
+ c = WindowContainer(title="Test")
+
+ c.add(TextWidget("Body"))
+ c.add(TextWidget("Body second line"))
+ c.render(30)
+
+ expected_result = [u"Test",
+ u"",
+ u"Body",
+ u"Body second line"]
+
+ res_lines = c.get_lines()
+ self.evaluate_result(res_lines, expected_result)
+
+ def test_window_container_wrapping(self):
+ c = WindowContainer(title="Test")
+
+ c.add(TextWidget("Body long line"))
+ c.add(TextWidget("Body"))
+ c.render(5)
+
+ expected_result = [u"Test",
+ u"",
+ u"Body",
+ u"long",
+ u"line",
+ u"Body"]
+
+ res_lines = c.get_lines()
+ self.evaluate_result(res_lines, expected_result)
+
+ def test_list_container_without_width(self):
+ column_count = 3
+ spacing_width = 3
+ c = ListRowContainer(column_count, spacing=spacing_width, numbering=False)
+
+ c.add(TextWidget("AAAA"))
+ c.add(TextWidget("BBBB"))
+ c.add(TextWidget("CCCCC")) # this line is too long
+ c.add(TextWidget("DDDD"))
+
+ expected_col_width = 4
+ expected_spacing_sum = 2 * spacing_width # three columns so 2 spacing between them
+ render_width = (column_count * expected_col_width) + expected_spacing_sum
+ c.render(render_width)
+
+ expected_result = [u"AAAA BBBB CCCC",
+ u" C",
+ u"DDDD"]
+
+ res_lines = c.get_lines()
+ self.evaluate_result(res_lines, expected_result)
+
+ def test_list_container_too_small(self):
+ # to be able to render this container we need at least 11 width
+ # 8 will take only spacing and then 1 for every column
+ c = ListRowContainer(3, spacing=4, numbering=False)
+
+ c.add(TextWidget("This can't be rendered."))
+ c.add(TextWidget("Because spacing takes more space than maximal width."))
+ c.add(TextWidget("Exception will raise."))
+
+ with self.assertRaisesRegex(ValueError, "Columns width is too small."):
+ c.render(10)
+
+ def test_list_container_too_small_turn_off_numbering(self):
+ # to be able to render this container we need
+ # 11 width + three times numbers (3 characters) = 20
+ #
+ # 8 will take only spacing and then 1 for every column
+ c = ListRowContainer(3, spacing=4, numbering=True)
+
+ c.add(TextWidget("This can't be rendered."))
+ c.add(TextWidget("Because spacing takes more space than maximal width."))
+ c.add(TextWidget("Exception will raise with info to turn off numbering."))
+
+ with self.assertRaisesRegex(ValueError, "Increase column width or disable numbering."):
+ c.render(19)
+
+
+@patch('simpleline.input.input_handler.InputHandlerRequest._get_input')
+@patch('sys.stdout', new_callable=StringIO)
+class ContainerInput_TestCase(unittest.TestCase):
+
+ def setUp(self):
+ self._callback_id = None
+ self._callback_called = None
+
+ def _prepare_callbacks(self, container, count):
+ for i in range(count):
+ container.add(TextWidget("Test"), self._callback, i + 1)
+
+ def test_list_widget_input_processing(self, out_mock, in_mock):
+ # call first container callback
+ in_mock.return_value = "2"
+
+ screen = ScreenWithListWidget(3)
+
+ App.initialize()
+ App.get_scheduler().schedule_screen(screen)
+ App.run()
+
+ self.assertEqual(1, screen.container_callback_input)
+
+ # TEST 0 or less as user input
+
+ def test_list_input_processing_input_0(self, out_mock, in_mock):
+ c = ListRowContainer(1)
+
+ self._prepare_callbacks(c, 3)
+
+ self.assertFalse(c.process_user_input("0"))
+
+ def test_list_input_processing_negative_number(self, out_mock, in_mock):
+ c = ListRowContainer(1)
+
+ self._prepare_callbacks(c, 3)
+
+ self.assertFalse(c.process_user_input("-2"))
+
+ def test_list_input_processing_exceeded(self, out_mock, in_mock):
+ c = ListRowContainer(1)
+
+ self._prepare_callbacks(c, 2)
+
+ self.assertFalse(c.process_user_input("3"))
+
+ def test_list_without_callback(self, out_mock, in_mock):
+ c = ListRowContainer(1)
+
+ c.add(TextWidget("Test"))
+
+ self.assertTrue(c.process_user_input("1"))
+
+ def test_list_callback_without_data(self, out_mock, in_mock):
+ c = ListRowContainer(1)
+
+ c.add(TextWidget("Test"), self._callback)
+
+ self.assertTrue(c.process_user_input("1"))
+ self.assertIsNone(self._callback_called)
+
+ def test_list_correct_input_processing(self, out_mock, in_mock):
+ c = ListRowContainer(1)
+
+ self._prepare_callbacks(c, 3)
+
+ self.assertTrue(c.process_user_input("2"))
+
+ self.assertEqual(self._callback_called, 2)
+
+ def test_list_wrong_input_processing(self, out_mock, in_mock):
+ c = ListRowContainer(1)
+
+ self._prepare_callbacks(c, 3)
+
+ self.assertFalse(c.process_user_input("c"))
+
+ def test_list_input_processing_none(self, out_mock, in_mock):
+ c = ListRowContainer(1)
+
+ self._prepare_callbacks(c, 2)
+
+ self.assertFalse(c.process_user_input(None))
+
+ def _callback(self, data):
+ self._callback_called = data
+
+
+class ScreenWithListWidget(UIScreen):
+
+ def __init__(self, widgets_count):
+ super().__init__()
+ self._widgets_count = widgets_count
+ self._list_widget = None
+ self.container_callback_input = -1
+
+ def refresh(self, args=None):
+ super().refresh(args)
+
+ self._list_widget = ListRowContainer(2)
+ for i in range(self._widgets_count):
+ self._list_widget.add(TextWidget("Test %s" % i), self._callback, i)
+
+ self.window.add(self._list_widget)
+
+ def input(self, args, key):
+ self.close()
+ if self._list_widget.process_user_input(key):
+ return InputState.PROCESSED
+
+ return InputState.DISCARDED
+
+ def _callback(self, data):
+ self.container_callback_input = data
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/tests/units/main/event_loop_test.py b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/tests/units/main/event_loop_test.py
new file mode 100644
index 0000000..92cfec0
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/tests/units/main/event_loop_test.py
@@ -0,0 +1,310 @@
+# Event loop test classes.
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+
+import unittest
+
+from simpleline.event_loop import AbstractSignal
+from simpleline.event_loop import EventHandler
+from simpleline.event_loop import ExitMainLoop
+from simpleline.event_loop.main_loop import MainLoop
+
+
+class EventLoopHandler_TestCase(unittest.TestCase):
+
+ def callback_func(self):
+ pass
+
+ def test_signal_handler_named_params(self):
+ data = [1, 2, "args"]
+ ev = EventHandler(callback=self.callback_func, data=data)
+
+ self.assertEqual(ev.callback, self.callback_func)
+ self.assertEqual(ev.data, data)
+
+ def test_signal_handler_positional_params(self):
+ data = [1, 3, "args"]
+ ev = EventHandler(self.callback_func, data)
+
+ self.assertEqual(ev.callback, self.callback_func)
+ self.assertEqual(ev.data, data)
+
+
+class ProcessEvents_TestCase(unittest.TestCase):
+
+ def setUp(self):
+ self.signal_counter = 0
+ self.signal_counter2 = 0
+ self.signal_counter_copied = 0
+ self.callback_called = False
+ self.callback_args = None
+ self.create_loop()
+
+ def create_loop(self):
+ self.loop = MainLoop()
+
+ def test_simple_register_handler(self):
+ self.callback_called = False
+
+ loop = self.loop
+ loop.register_signal_handler(TestSignal, self._handler_callback)
+ loop.enqueue_signal(TestSignal())
+ loop.process_signals()
+
+ self.assertTrue(self.callback_called)
+
+ def test_process_more_signals(self):
+ self.signal_counter = 0
+
+ loop = self.loop
+ loop.register_signal_handler(TestSignal, self._handler_signal_counter)
+ loop.enqueue_signal(TestSignal())
+ loop.enqueue_signal(TestSignal())
+ loop.enqueue_signal(TestSignal())
+ loop.process_signals()
+
+ self.assertEqual(self.signal_counter, 3)
+
+ def test_process_signals_multiple_times(self):
+ self.signal_counter = 0
+
+ loop = self.loop
+ loop.register_signal_handler(TestSignal, self._handler_signal_counter)
+ loop.enqueue_signal(TestSignal())
+ loop.enqueue_signal(TestSignal())
+ loop.process_signals()
+ self.assertEqual(self.signal_counter, 2)
+
+ loop.enqueue_signal(TestSignal())
+ loop.enqueue_signal(TestSignal())
+ loop.process_signals()
+ self.assertEqual(self.signal_counter, 4)
+
+ def test_wait_on_signal(self):
+ self.signal_counter = 0
+
+ loop = self.loop
+ loop.register_signal_handler(TestSignal,
+ self._handler_signal_counter)
+ loop.register_signal_handler(TestSignal2,
+ self._handler_process_events_then_register_testsignal,
+ loop)
+ loop.enqueue_signal(TestSignal())
+ loop.enqueue_signal(TestSignal2())
+ loop.process_signals(return_after=TestSignal2)
+ self.assertEqual(self.signal_counter, 1)
+
+ loop.process_signals()
+ self.assertEqual(self.signal_counter, 2)
+
+ def test_wait_on_signal_skipped_by_inner_process_events(self):
+ self.signal_counter = 0
+
+ loop = self.loop
+ loop.register_signal_handler(TestSignal,
+ self._handler_signal_counter)
+ # run process signals recursively in this handler which will skip processing
+ loop.register_signal_handler(TestSignal2,
+ self._handler_process_events_then_register_testsignal,
+ loop)
+ loop.enqueue_signal(TestSignal2())
+ loop.enqueue_signal(TestSignal())
+ # new signal will be registered in handler method but that shouldn't be processed
+ # because it should end on the first signal even when it was skipped
+ loop.process_signals(return_after=TestSignal)
+
+ self.assertEqual(self.signal_counter, 1)
+
+ def test_multiple_handlers_to_signal(self):
+ self.signal_counter = 0
+ self.signal_counter2 = 0
+
+ loop = self.loop
+ loop.register_signal_handler(TestSignal, self._handler_signal_counter)
+ loop.register_signal_handler(TestSignal, self._handler_signal_counter2)
+ loop.enqueue_signal(TestSignal())
+ loop.enqueue_signal(TestSignal())
+ loop.process_signals()
+
+ self.assertEqual(self.signal_counter, 2)
+ self.assertEqual(self.signal_counter2, 2)
+
+ def test_priority_signal_processing(self):
+ self.signal_counter = 0
+
+ loop = self.loop
+ loop.register_signal_handler(TestSignal, self._handler_signal_counter)
+ loop.register_signal_handler(TestPrioritySignal, self._handler_signal_counter)
+ loop.enqueue_signal(TestSignal())
+ loop.enqueue_signal(TestSignal())
+ # should be processed as first signal because of priority
+ loop.enqueue_signal(TestPrioritySignal())
+ loop.enqueue_signal(TestSignal())
+ loop.process_signals()
+ self.assertEqual(self.signal_counter, 1)
+
+ # process rest of the signals
+ loop.process_signals()
+ self.assertEqual(self.signal_counter, 4)
+
+ def test_low_priority_signal_processing(self):
+ self.signal_counter = 0
+ self.signal_counter_copied = 0
+
+ loop = self.loop
+ loop.register_signal_handler(TestSignal, self._handler_signal_counter)
+ loop.register_signal_handler(TestLowPrioritySignal, self._handler_signal_copy_counter)
+ loop.enqueue_signal(TestSignal())
+ loop.enqueue_signal(TestLowPrioritySignal())
+ loop.enqueue_signal(TestSignal())
+ loop.enqueue_signal(TestSignal())
+ loop.process_signals()
+ self.assertEqual(self.signal_counter, 3)
+
+ # process the low priority signal
+ loop.process_signals()
+ self.assertEqual(self.signal_counter_copied, 3)
+
+ def test_quit_callback(self):
+ self.callback_called = False
+ self.callback_args = None
+ msg = "Test data"
+
+ loop = self.loop
+ loop.set_quit_callback(self._handler_quit_callback, args=msg)
+ loop.register_signal_handler(TestSignal, self._handler_raise_ExitMainLoop_exception)
+ loop.enqueue_signal(TestSignal())
+ loop.run()
+
+ self.assertTrue(self.callback_called)
+ self.assertEqual(msg, self.callback_args)
+
+ def test_force_quit(self):
+ self.callback_called = False
+
+ loop = self.loop
+ loop.register_signal_handler(TestSignal, self._handler_callback)
+ loop.register_signal_handler(TestSignal2, self._handler_force_quit_exception)
+ loop.enqueue_signal(TestSignal2())
+ loop.enqueue_signal(TestSignal())
+ loop.run()
+
+ self.assertFalse(self.callback_called)
+
+ def test_force_quit_recursive_loop(self):
+ self.callback_called = False
+
+ loop = self.loop
+ loop.register_signal_handler(TestSignal,
+ self._handler_start_inner_loop_and_enqueue_event,
+ TestSignal3())
+ loop.register_signal_handler(TestSignal2,
+ self._handler_callback)
+ loop.register_signal_handler(TestSignal3,
+ self._handler_force_quit_exception)
+ loop.enqueue_signal(TestSignal())
+ loop.enqueue_signal(TestSignal2())
+ loop.run()
+
+ self.assertFalse(self.callback_called)
+
+ def test_force_quit_when_waiting_on_signal(self):
+ self.callback_called = False
+
+ loop = self.loop
+ loop.register_signal_handler(TestSignal, self._handler_force_quit_exception)
+ loop.register_signal_handler(TestSignal2, self._handler_callback)
+ loop.enqueue_signal(TestSignal())
+ loop.enqueue_signal(TestSignal2())
+
+ # FIXME: Find a better way how to detect infinite loop
+ # if force quit won't work properly this will hang up
+ loop.process_signals(return_after=TestSignal3)
+
+ self.assertFalse(self.callback_called)
+
+ # HANDLERS FOR TESTING
+ def _handler_callback(self, signal, data):
+ self.callback_called = True
+
+ def _handler_quit_callback(self, args):
+ self.callback_called = True
+ self.callback_args = args
+
+ def _handler_signal_counter(self, signal, data):
+ self.signal_counter += 1
+
+ def _handler_signal_counter2(self, signal, data):
+ self.signal_counter2 += 1
+
+ def _handler_signal_copy_counter(self, signal, data):
+ self.signal_counter_copied = self.signal_counter
+
+ @staticmethod
+ def _handler_process_events_then_register_testsignal(signal, data):
+ event_loop = data
+ event_loop.process_signals()
+ # This shouldn't be processed
+ event_loop.enqueue_signal(TestSignal())
+
+ def _handler_start_inner_loop_and_enqueue_event(self, signal, data):
+ self.loop.execute_new_loop(data)
+
+ @staticmethod
+ def _handler_raise_ExitMainLoop_exception(signal, data):
+ raise ExitMainLoop()
+
+ def _handler_force_quit_exception(self, signal, data):
+ self.loop.force_quit()
+
+
+# TESTING EVENTS
+class TestSignal(AbstractSignal):
+
+ def __init__(self):
+ # ignore source
+ super().__init__(None)
+
+
+class TestSignal2(AbstractSignal):
+
+ def __init__(self):
+ # ignore source
+ super().__init__(None)
+
+
+class TestSignal3(AbstractSignal):
+
+ def __init__(self):
+ # ignore source
+ super().__init__(None)
+
+
+class TestPrioritySignal(AbstractSignal):
+
+ def __init__(self):
+ # ignore source
+ super().__init__(None, -10)
+
+
+class TestLowPrioritySignal(AbstractSignal):
+
+ def __init__(self):
+ # ignore source
+ super().__init__(None, 20)
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/tests/units/main/event_queue_test.py b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/tests/units/main/event_queue_test.py
new file mode 100644
index 0000000..7707658
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/tests/units/main/event_queue_test.py
@@ -0,0 +1,105 @@
+# Event queue test classes.
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+
+
+import unittest
+
+from unittest.mock import MagicMock
+
+from simpleline.event_loop.event_queue import EventQueue, EventQueueError
+from simpleline.event_loop.signals import AbstractSignal
+
+
+class EventQueue_TestCase(unittest.TestCase):
+
+ def setUp(self):
+ self.e = EventQueue()
+
+ def test_queue_is_empty(self):
+ self.assertTrue(self.e.empty())
+
+ def test_enqueue(self):
+ fake_signal = MagicMock()
+
+ self.e.enqueue(fake_signal)
+ self.assertFalse(self.e.empty())
+
+ self.assertEqual(fake_signal, self.e.get())
+ self.assertTrue(self.e.empty())
+
+ def test_enqueue_priority(self):
+ signal_low_priority = TestSignal(priority=10)
+ signal_high_priority = TestSignal(priority=0)
+
+ self.e.enqueue(signal_low_priority)
+ self.e.enqueue(signal_high_priority)
+
+ self.assertEqual(signal_high_priority, self.e.get())
+ self.assertEqual(signal_low_priority, self.e.get())
+
+ # Test adding signals in different order (result shouldn't change)
+ self.e.enqueue(signal_high_priority)
+ self.e.enqueue(signal_low_priority)
+
+ self.assertEqual(signal_high_priority, self.e.get())
+ self.assertEqual(signal_low_priority, self.e.get())
+
+ def test_adding_event_source(self):
+ fake_source = MagicMock()
+ self.e.add_source(fake_source)
+
+ self.assertTrue(self.e.contains_source(fake_source))
+
+ def test_removing_event_source(self):
+ fake_source = MagicMock()
+ self.e.add_source(fake_source)
+
+ self.e.remove_source(fake_source)
+
+ self.assertFalse(self.e.contains_source(fake_source))
+
+ def test_remove_empty_source(self):
+ with self.assertRaises(EventQueueError):
+ self.e.remove_source(MagicMock())
+
+ def test_enqueue_if_source_belongs(self):
+ source = MagicMock()
+ signal = TestSignal(source=source)
+
+ self.e.add_source(source)
+ self.assertTrue(self.e.enqueue_if_source_belongs(signal, source))
+ self.assertEqual(signal, self.e.get())
+
+ def test_enqueue_if_source_does_not_belong(self):
+ signal = TestSignal()
+ signal_low_priority = TestSignal(priority=25)
+
+ # the get method will wait if nothing present so adding low priority signal below
+ # give us check if the queue is really empty
+ self.e.enqueue(signal_low_priority)
+
+ self.assertFalse(self.e.enqueue_if_source_belongs(signal, MagicMock()))
+ self.assertEqual(signal_low_priority, self.e.get())
+
+
+class TestSignal(AbstractSignal):
+
+ def __init__(self, source=None, priority=20): # pylint: disable=useless-super-delegation
+ super().__init__(source, priority)
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/tests/units/main/global_configuration_test.py b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/tests/units/main/global_configuration_test.py
new file mode 100644
index 0000000..4af5fad
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/tests/units/main/global_configuration_test.py
@@ -0,0 +1,84 @@
+# GlobalConfiguration class test classes.
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+
+import unittest
+
+from unittest.mock import MagicMock
+
+from simpleline import App
+from simpleline.global_configuration import DEFAULT_WIDTH, DEFAULT_PASSWORD_FUNC
+
+
+class GlobalConfiguration_TestCase(unittest.TestCase):
+
+ def tearDown(self):
+ App.initialize()
+
+ def _check_default_width(self, width=DEFAULT_WIDTH):
+ self.assertEqual(App.get_configuration().width, width)
+
+ def _check_default_password_function(self, password_func=None):
+ if password_func:
+ self.assertEqual(App.get_configuration().password_function, password_func)
+ else:
+ self.assertEqual(App.get_configuration().password_function, DEFAULT_PASSWORD_FUNC)
+
+ def test_clear_width(self):
+ self._check_default_width()
+
+ test_width = 150
+
+ App.get_configuration().width = test_width
+ self._check_default_width(test_width)
+
+ App.get_configuration().clear_width()
+ self._check_default_width()
+
+ def test_width(self):
+ self._check_default_width()
+
+ App.initialize()
+ App.get_configuration().width = 100
+ self._check_default_width(100)
+
+ App.initialize()
+ self._check_default_width()
+
+ def test_password_function(self):
+ self._check_default_password_function()
+
+ test_mock = MagicMock()
+ App.initialize()
+ App.get_configuration().password_function = test_mock
+ self._check_default_password_function(test_mock)
+
+ App.initialize()
+ self._check_default_password_function()
+
+ def test_clear_password_function(self):
+ self._check_default_password_function()
+
+ test_func = MagicMock()
+
+ App.get_configuration().password_function = test_func
+ self._check_default_password_function(test_func)
+
+ App.get_configuration().clear_password_function()
+ self._check_default_password_function()
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/tests/units/main/input_handler_test.py b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/tests/units/main/input_handler_test.py
new file mode 100644
index 0000000..9e466b1
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/tests/units/main/input_handler_test.py
@@ -0,0 +1,247 @@
+# Rendering screen test classes.
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+
+
+import unittest
+
+from unittest.mock import Mock, patch
+from threading import Barrier, current_thread, Event
+
+from io import StringIO
+
+from simpleline import App
+from simpleline.event_loop.main_loop import MainLoop
+from simpleline.input.input_handler import InputHandler, PasswordInputHandler
+from simpleline.render.prompt import Prompt
+
+
+@patch('sys.stdout', new_callable=StringIO)
+@patch('simpleline.input.input_handler.InputHandlerRequest._get_input')
+class InputHandler_TestCase(unittest.TestCase):
+
+ def create_loop(self):
+ self.loop = MainLoop()
+
+ def setUp(self):
+ super().setUp()
+ self.create_loop()
+ App.initialize(event_loop=self.loop)
+
+ self._callback_called = False
+ self._callback_input = ""
+
+ self._callback_called2 = False
+ self._callback_input2 = ""
+
+ self._thread_barrier = Barrier(2, timeout=3)
+ self._thread_event_wait_for_inner = Event()
+ self._thread_event_wait_for_outer = Event()
+ self._threads = []
+
+ def tearDown(self):
+ super().tearDown()
+ self._thread_event_wait_for_outer.set()
+
+ for t in self._threads:
+ t.join()
+
+ # process InputReceivedSignal
+ App.get_event_loop().process_signals()
+ # process InputReadySignal
+ App.get_event_loop().process_signals()
+
+ def test_async_input(self, input_mock, output_mock):
+ input_mock.return_value = 'a'
+
+ h = InputHandler()
+ h.get_input(Prompt(message="ABC"))
+ h.wait_on_input()
+
+ self.assertEqual(h.value, 'a')
+
+ def test_input_received(self, input_mock, output_mock):
+ input_mock.return_value = 'a'
+
+ h = InputHandler()
+
+ self.assertFalse(h.input_received())
+
+ h.get_input(Prompt(message="ABC"))
+ h.wait_on_input()
+
+ self.assertTrue(h.input_received())
+
+ def test_input_callback(self, input_mock, output_mock):
+ input_value = 'abc'
+ input_mock.return_value = input_value
+
+ h = InputHandler()
+ h.set_callback(self._test_callback)
+ h.get_input(Prompt(message="ABC"))
+ h.wait_on_input()
+
+ self.assertTrue(self._callback_called)
+ self.assertEqual(self._callback_input, input_value)
+ self.assertEqual(h.value, input_value)
+
+ def test_concurrent_input(self, input_mock, output_mock):
+ input_mock.side_effect = self._wait_for_concurrent_call
+
+ h = InputHandler()
+ h.set_callback(self._test_callback)
+ h2 = InputHandler()
+ h2.set_callback(self._test_callback2)
+
+ with self.assertRaisesRegex(KeyError, r'.*Input handler:.*InputHandler object'
+ r'.*Input requester: Unknown'
+ r'.*Input handler:.*InputHandler object'
+ r'.*Input requester: Unknown.*'):
+ h.get_input(Prompt(message="ABC"))
+ self._thread_event_wait_for_inner.wait()
+ h2.get_input(Prompt(message="ABC"))
+
+ self.assertFalse(self._callback_called)
+ self.assertEqual(self._callback_input, "")
+ self.assertEqual(h.value, None)
+
+ self.assertFalse(self._callback_called2)
+ self.assertEqual(self._callback_input2, "")
+ self.assertEqual(h2.value, None)
+
+ def test_concurrent_input_with_source(self, input_mock, output_mock):
+ input_mock.side_effect = self._wait_for_concurrent_call
+ source1 = Mock()
+ source2 = Mock()
+
+ h = InputHandler(source=source1)
+ h2 = InputHandler(source=source2)
+ h.set_callback(self._test_callback)
+ h2.set_callback(self._test_callback2)
+
+ with self.assertRaisesRegex(KeyError, r'.*Input handler:.*InputHandler object'
+ r'.*Input requester:.*Mock'
+ r'.*Input handler:.*InputHandler object'
+ r'.*Input requester:.*Mock.*'):
+ h.get_input(Prompt(message="ABC"))
+ self._thread_event_wait_for_inner.wait()
+ h2.get_input(Prompt(message="ABC"))
+
+ self.assertFalse(self._callback_called)
+ self.assertEqual(self._callback_input, "")
+ self.assertEqual(h.value, None)
+
+ self.assertFalse(self._callback_called2)
+ self.assertEqual(self._callback_input2, "")
+ self.assertEqual(h2.value, None)
+
+ def test_concurrent_input_without_check(self, input_mock, output_mock):
+ input_mock.side_effect = self._wait_for_concurrent_call
+
+ h = InputHandler()
+ h2 = InputHandler()
+ h.set_callback(self._test_callback)
+ h2.set_callback(self._test_callback2)
+ h2.skip_concurrency_check = True
+ h.skip_concurrency_check = True
+
+ h.get_input(Prompt(message="ABC"))
+ self._thread_event_wait_for_inner.wait()
+ h2.get_input(Prompt(message="ABC"))
+ self._thread_event_wait_for_outer.set()
+
+ h.wait_on_input()
+ h2.wait_on_input()
+
+ self.assertFalse(self._callback_called)
+ self.assertFalse(h.input_successful())
+
+ self.assertTrue(self._callback_called2)
+ self.assertTrue(h2.input_successful())
+ self.assertEqual(self._callback_input2, "thread 0")
+ self.assertEqual(h2.value, "thread 0")
+
+ def _wait_for_concurrent_call(self):
+ ret = "thread {}".format(len(self._threads))
+ self._threads.append(current_thread())
+ self._thread_event_wait_for_inner.set()
+ self._thread_event_wait_for_outer.wait()
+ return ret
+
+ def _test_callback(self, user_input):
+ self._callback_called = True
+ self._callback_input = user_input
+
+ def _test_callback2(self, user_input):
+ self._callback_called2 = True
+ self._callback_input2 = user_input
+
+
+@patch('sys.stdout', new_callable=StringIO)
+@patch('simpleline.global_configuration.GlobalConfiguration.password_function')
+class PasswordInputHandler_TestCase(unittest.TestCase):
+
+ def create_loop(self):
+ self.loop = MainLoop()
+
+ def setUp(self):
+ super().setUp()
+ self.create_loop()
+ App.initialize(event_loop=self.loop)
+
+ self._callback_called = False
+ self._callback_input = ""
+
+ def test_async_input(self, input_mock, output_mock):
+ input_mock.return_value = 'a'
+
+ h = PasswordInputHandler()
+ h.get_input(Prompt(message="ABC"))
+ h.wait_on_input()
+
+ self.assertEqual(h.value, 'a')
+
+ def test_input_received(self, input_mock, output_mock):
+ input_mock.return_value = 'a'
+
+ h = PasswordInputHandler()
+
+ self.assertFalse(h.input_received())
+
+ h.get_input(Prompt(message="ABC"))
+ h.wait_on_input()
+
+ self.assertTrue(h.input_received())
+
+ def test_input_callback(self, input_mock, output_mock):
+ input_value = 'abc'
+ input_mock.return_value = input_value
+
+ h = PasswordInputHandler()
+ h.set_callback(self._test_callback)
+ h.get_input(Prompt(message="ABC"))
+ h.wait_on_input()
+
+ self.assertTrue(self._callback_called)
+ self.assertEqual(self._callback_input, input_value)
+ self.assertEqual(h.value, input_value)
+
+ def _test_callback(self, user_input):
+ self._callback_called = True
+ self._callback_input = user_input
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/tests/units/main/prompt_test.py b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/tests/units/main/prompt_test.py
new file mode 100644
index 0000000..a5751a5
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/tests/units/main/prompt_test.py
@@ -0,0 +1,135 @@
+# Prompt test classes.
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+
+
+import unittest
+
+from simpleline.render.prompt import Prompt
+
+
+class Prompt_TestCase(unittest.TestCase):
+ def test_prompt_message(self):
+ p = Prompt()
+
+ self.assertEqual(p.message, Prompt.DEFAULT_MESSAGE)
+
+ p.set_message("Brand new message")
+ self.assertEqual(p.message, "Brand new message")
+
+ p.set_message(u"Žluťoučký kůň")
+ self.assertEqual(p.message, u"Žluťoučký kůň")
+
+ p2 = Prompt("Default prompt text")
+ self.assertEqual(p2.message, "Default prompt text")
+
+ def test_add_options(self):
+ p = Prompt()
+
+ # add new option
+ p.add_option("R", "refresh")
+ self.assertTrue("R" in p.options)
+ self.assertEqual(p.options["R"], "refresh")
+
+ # add option over the existing option should trigger warning message
+ with self.assertLogs("simpleline", level="WARNING"):
+ p.add_option("R", "another refresh")
+
+ # update existing option
+ p.update_option("R", "new refresh option")
+ self.assertEqual(p.options["R"], "new refresh option")
+
+ # update non existing option should trigger warning
+ with self.assertLogs("simpleline", level="WARNING"):
+ p.update_option("N", "non existing option")
+
+ p = Prompt()
+ p.add_option("N", "new option")
+ # remove option
+ ret = p.remove_option("N")
+ self.assertFalse("N" in p.options)
+ self.assertEqual(ret, "new option")
+
+ # remove non-existing option
+ ret = p.remove_option("non-existing")
+ self.assertIsNone(ret)
+
+ def _check_default_option(self, prompt, key, value):
+ self.assertEqual(len(prompt.options), 1)
+ self.assertEqual(prompt.options[key], value)
+
+ def test_refresh_option(self):
+ # refresh option
+ p = Prompt()
+ p.add_refresh_option()
+ self._check_default_option(p, Prompt.REFRESH, Prompt.REFRESH_DESCRIPTION)
+
+ # test add with description
+ p = Prompt()
+ p.add_refresh_option("Other refresh")
+ self._check_default_option(p, Prompt.REFRESH, "Other refresh")
+
+ # change existing description
+ p.add_refresh_option("New refresh")
+ self._check_default_option(p, Prompt.REFRESH, "New refresh")
+
+ def test_continue_option(self):
+ # continue option
+ p = Prompt()
+ p.add_continue_option()
+ self._check_default_option(p, Prompt.CONTINUE, Prompt.CONTINUE_DESCRIPTION)
+
+ # test add with description
+ p = Prompt()
+ p.add_continue_option("Other continue")
+ self._check_default_option(p, Prompt.CONTINUE, "Other continue")
+
+ # change existing description
+ p.add_continue_option("New continue")
+ self._check_default_option(p, Prompt.CONTINUE, "New continue")
+
+ def test_quit_option(self):
+ # quit option
+ p = Prompt()
+ p.add_quit_option()
+ self._check_default_option(p, Prompt.QUIT, Prompt.QUIT_DESCRIPTION)
+
+ # test add with description
+ p = Prompt()
+ p.add_quit_option("Other quit")
+ self._check_default_option(p, Prompt.QUIT, "Other quit")
+
+ # change existing description
+ p.add_quit_option("New quit")
+ self._check_default_option(p, Prompt.QUIT, "New quit")
+
+ def test_help_option(self):
+ # help option
+ p = Prompt()
+ p.add_help_option()
+ self._check_default_option(p, Prompt.HELP, Prompt.HELP_DESCRIPTION)
+
+ # test add with description
+ p = Prompt()
+ p.add_help_option("Other help")
+ self._check_default_option(p, Prompt.HELP, "Other help")
+
+ # change existing description
+ p.add_help_option("New help")
+ self._check_default_option(p, Prompt.HELP, "New help")
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/tests/units/main/render_screen_test.py b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/tests/units/main/render_screen_test.py
new file mode 100644
index 0000000..c27a951
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/tests/units/main/render_screen_test.py
@@ -0,0 +1,564 @@
+# Rendering screen test classes.
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+
+
+import unittest
+from io import StringIO
+from unittest import mock
+
+from simpleline import App
+from simpleline.render import RenderUnexpectedError
+from simpleline.render.screen import UIScreen, InputState
+
+from .. import UtilityMixin
+
+
+def _fake_input(queue_instance, prompt):
+ queue_instance.put("a")
+
+
+@mock.patch('sys.stdout', new_callable=StringIO)
+class SeparatorPrinting_TestCase(unittest.TestCase, UtilityMixin):
+
+ def setUp(self):
+ App.initialize()
+
+ def test_separator(self, stdout_mock):
+ ui_screen = EmptyScreen()
+
+ self.schedule_screen_and_run(ui_screen)
+
+ self.assertEqual(self.calculate_separator(), stdout_mock.getvalue())
+
+ def test_other_width_separator(self, stdout_mock):
+ ui_screen = EmptyScreen()
+ width = 60
+
+ App.get_configuration().width = width
+ App.get_scheduler().schedule_screen(ui_screen)
+ App.run()
+
+ self.assertEqual(self.calculate_separator(width), stdout_mock.getvalue())
+
+ def test_zero_width_no_separator(self, stdout_mock):
+ ui_screen = EmptyScreen()
+ width = 0
+
+ App.get_configuration().width = width
+ App.get_scheduler().schedule_screen(ui_screen)
+ App.run()
+
+ self.assertEqual("\n\n", stdout_mock.getvalue())
+
+ def test_no_separator_when_screen_setup_fails(self, stdout_mock):
+ ui_screen = TestScreenSetupFail()
+
+ App.initialize()
+ App.get_scheduler().schedule_screen(ui_screen)
+ App.run()
+
+ self.assertEqual("", stdout_mock.getvalue())
+
+ def test_no_separator(self, stdout_mock):
+ print_text = "testing"
+ screen = NoSeparatorScreen(print_text)
+
+ self.schedule_screen_and_run(screen)
+ self.schedule_screen_and_run(screen)
+
+ expected_output = print_text + "\n" + print_text + "\n"
+
+ self.assertEqual(expected_output, stdout_mock.getvalue())
+
+
+class SimpleUIScreenFeatures_TestCase(unittest.TestCase):
+
+ def test_close_screen(self):
+ screen = UIScreen()
+
+ App.initialize()
+ App.get_scheduler().schedule_screen(screen)
+ # Program will quit in close_screen when stack is empty
+ App.get_scheduler().schedule_screen(UIScreen())
+ screen.close()
+
+ def test_close_screen_closed_from_other_source_error(self):
+ App.initialize()
+ App.get_scheduler().schedule_screen(UIScreen())
+ with self.assertRaises(RenderUnexpectedError):
+ App.get_scheduler().close_screen(closed_from=mock.MagicMock())
+
+ def test_failed_screen_setup(self):
+ screen = FailedSetupScreen()
+
+ App.initialize()
+ App.get_scheduler().schedule_screen(screen)
+ App.run()
+
+
+@mock.patch('sys.stdout', new_callable=StringIO)
+class SimpleUIScreenProcessing_TestCase(unittest.TestCase, UtilityMixin):
+
+ def setUp(self):
+ self._default_separator = self.calculate_separator(80)
+
+ def test_screen_event_loop_processing(self, _):
+ ui_screen = EmptyScreen()
+
+ self.schedule_screen_and_run(ui_screen)
+
+ self.assertTrue(ui_screen.is_closed)
+
+ def test_running_empty_loop(self, _):
+ App.initialize()
+ loop = App.get_event_loop()
+ loop.process_signals()
+
+ def test_screen_event_loop_processing_with_two_screens(self, _):
+ first_screen = EmptyScreen()
+ screen = EmptyScreen()
+
+ App.initialize()
+ App.get_scheduler().schedule_screen(first_screen)
+ App.get_scheduler().schedule_screen(screen)
+ App.run()
+
+ self.assertTrue(first_screen)
+ self.assertTrue(screen)
+
+ def test_screen_title_rendering(self, stdout_mock):
+ screen = NoInputScreen()
+ screen.title = u"TestTitle"
+
+ self.schedule_screen_and_run(screen)
+
+ out = self._default_separator
+ out += "TestTitle\n\n"
+ self.assertEqual(stdout_mock.getvalue(), out)
+
+
+@mock.patch('sys.stdout', new_callable=StringIO)
+@mock.patch('simpleline.event_loop.AbstractEventLoop.kill_app_with_traceback')
+class ScreenException_TestCase(unittest.TestCase, UtilityMixin):
+
+ def setUp(self):
+ self._force_quit_called = False
+
+ # The original method calls sys.exit(1) so we don't need to test this functionality
+ def force_quit_mock(self, signal, data=None):
+ self._force_quit_called = True
+ loop = App.get_event_loop()
+ loop.force_quit()
+
+ def test_raise_exception_in_refresh(self, mock_kill_app, _):
+ screen = ExceptionTestScreen(ExceptionTestScreen.REFRESH)
+ mock_kill_app.side_effect = self.force_quit_mock
+
+ self.schedule_screen_and_run(screen)
+ self.assertTrue(self._force_quit_called)
+
+ def test_raise_exception_in_rendering(self, mock_kill_app, _):
+ screen = ExceptionTestScreen(ExceptionTestScreen.REDRAW)
+ mock_kill_app.side_effect = self.force_quit_mock
+
+ self.schedule_screen_and_run(screen)
+ self.assertTrue(self._force_quit_called)
+
+
+@mock.patch('sys.stdout', new_callable=StringIO)
+@mock.patch('simpleline.input.input_handler.InputHandlerRequest._get_input')
+class InputProcessing_TestCase(unittest.TestCase):
+
+ def setUp(self):
+ App.initialize()
+
+ def test_basic_input(self, input_mock, mock_stdout):
+ input_mock.return_value = "a"
+ screen = InputScreen()
+
+ App.get_scheduler().schedule_screen(screen)
+ App.run()
+
+ self.assertTrue(screen.input_processed)
+
+ def test_process_input_and_redraw(self, input_mock, mock_stdout):
+ input_mock.return_value = "a"
+ screen = InputStateRedrawScreen()
+
+ App.get_scheduler().schedule_screen(screen)
+ App.run()
+
+ self.assertTrue(screen.refreshed)
+
+ def test_process_input_and_close(self, input_mock, mock_stdout):
+ input_mock.return_value = "a"
+ screen = InputStateCloseScreen()
+
+ App.get_scheduler().schedule_screen(screen)
+ App.run()
+
+ self.assertTrue(screen.input_processed)
+
+ def test_quit_input(self, mock_stdin, mock_stdout):
+ mock_stdin.return_value = "q"
+ screen = UIScreen()
+
+ App.get_scheduler().schedule_screen(screen)
+ App.run()
+
+ def test_continue_input(self, mock_stdin, mock_stdout):
+ mock_stdin.return_value = "c"
+ screen = UIScreen()
+ screen2 = EmptyScreen()
+
+ App.get_scheduler().schedule_screen(screen)
+ App.get_scheduler().schedule_screen(screen2)
+ App.run()
+
+ self.assertTrue(screen.screen_ready)
+ self.assertTrue(screen.screen_ready)
+
+ def test_refresh_input(self, mock_stdin, mock_stdout):
+ mock_stdin.return_value = "r"
+ screen = RefreshTestScreen()
+
+ App.get_scheduler().schedule_screen(screen)
+ App.run()
+
+ self.assertTrue(screen.input_processed)
+
+ def test_refresh_on_input_error(self, mock_stdin, mock_stdout):
+ mock_stdin.return_value = "q"
+ threshold = 5
+ screen = InputErrorTestScreen(threshold)
+
+ App.get_scheduler().schedule_screen(screen)
+ App.run()
+
+ self.assertEqual(screen.render_counter, 2)
+ self.assertEqual(screen.error_counter, threshold)
+
+ def test_multiple_refresh_on_input_error(self, mock_stdin, mock_stdout):
+ mock_stdin.return_value = "q"
+ threshold = 12
+ screen = InputErrorTestScreen(threshold)
+
+ App.get_scheduler().schedule_screen(screen)
+ App.run()
+
+ self.assertEqual(screen.render_counter, 3)
+ self.assertEqual(screen.error_counter, threshold)
+
+ def test_no_refresh_when_prompt_is_none(self, mock_stdin, mock_stdout):
+ mock_stdin.return_value = "q"
+ threshold = 5
+ screen = InputErrorDynamicPromptTestScreen(threshold, not_return_prompt_on=3)
+
+ App.get_scheduler().schedule_screen(screen)
+ App.run()
+
+ # first draw and manual redraw when prompt is None
+ self.assertEqual(screen.render_counter, 2)
+ self.assertTrue(screen.input_skipped)
+ self.assertEqual(screen.error_counter, threshold)
+
+ def test_input_no_prompt(self, mock_stdin, mock_stdout):
+ screen = InputWithNoPrompt()
+
+ App.get_scheduler().schedule_screen(screen)
+ App.run()
+
+ self.assertTrue(screen.prompt_entered)
+
+ @mock.patch('simpleline.event_loop.main_loop.MainLoop.process_signals')
+ def test_custom_getpass(self, mock_stdin, mock_stdout, process_signals):
+ prompt = mock.MagicMock()
+ ret = "test"
+ screen = TestScreenWithPassFunc(prompt, ret)
+
+ App.get_scheduler().schedule_screen(screen)
+ App.run()
+
+ self.assertTrue(screen.pass_called)
+ self.assertEqual(screen.pass_prompt.rstrip(), str(prompt))
+
+ def test_blocking_input(self, mock_stdin, mock_stdout):
+ prompt_message = "test prompt"
+ ret = "blocking test"
+ mock_stdin.return_value = ret
+ screen = BlockingInputTestScreen(prompt_message, False)
+
+ App.get_scheduler().schedule_screen(screen)
+ App.run()
+
+ self.assertEqual(screen.input_returned, ret)
+
+ out = mock_stdout.getvalue()
+ out = out.split("\n")[-1].strip()
+ self.assertEqual(out, prompt_message)
+
+ @mock.patch('simpleline.global_configuration.GlobalConfiguration.password_function')
+ def test_blocking_password_input(self, mock_getpass, mock_stdin, mock_stdout):
+ prompt_message = "test prompt"
+ ret = "blocking test"
+ mock_getpass.return_value = ret
+ screen = BlockingInputTestScreen(prompt_message, True)
+
+ App.get_scheduler().schedule_screen(screen)
+ App.run()
+
+ self.assertEqual(screen.input_returned, ret)
+
+ out = mock_stdout.getvalue()
+ # can't check for prompt because that is printed by getpass func which is mocked
+ self.assertGreater(len(out), 1)
+
+
+# HELPER CLASSES
+
+class EmptyScreen(UIScreen):
+
+ def __init__(self):
+ super().__init__()
+ self.is_closed = False
+ EmptyScreen.title = ""
+ self.input_required = False
+
+ def show_all(self):
+ self.close()
+
+ def closed(self):
+ self.is_closed = True
+
+
+class TestScreenSetupFail(UIScreen):
+
+ def __init__(self):
+ super().__init__()
+ self.input_required = False
+
+ def setup(self, args):
+ super().setup(args)
+ return False
+
+
+class TestScreenWithPassFunc(UIScreen):
+
+ def __init__(self, prompt, return_value):
+ super().__init__()
+ self.pass_prompt = ""
+ self.pass_called = False
+ self.password_func = self._test_getpass
+ self.hide_user_input = True
+ self._prompt = prompt
+ self._return_value = return_value
+
+ def prompt(self, args=None):
+ return self._prompt
+
+ def _test_getpass(self, prompt):
+ self.pass_prompt = prompt
+ self.pass_called = True
+ return self._return_value
+
+ def input(self, args, key):
+ return InputState.PROCESSED_AND_CLOSE
+
+
+class InputErrorTestScreen(UIScreen):
+
+ def __init__(self, error_threshold=5):
+ super().__init__()
+ self.error_counter = 0
+ self.render_counter = 0
+ self._error_threshold = error_threshold
+
+ def input(self, args, key):
+ if self.error_counter == self._error_threshold:
+ # let "q" propagate to quit
+ return key
+
+ self.error_counter += 1
+ return InputState.DISCARDED
+
+ def show_all(self):
+ self.render_counter += 1
+
+
+class InputErrorDynamicPromptTestScreen(InputErrorTestScreen):
+
+ def __init__(self, error_threshold=5, not_return_prompt_on=2):
+ super().__init__(error_threshold=error_threshold)
+ self._not_return_prompt_on = not_return_prompt_on
+ self.input_skipped = False
+
+ def prompt(self, args=None):
+ if self.error_counter == self._not_return_prompt_on and not self.input_skipped:
+ self.input_skipped = True
+ self.redraw()
+ return None
+
+ return super().prompt(args)
+
+
+class InputWithNoPrompt(UIScreen):
+
+ def __init__(self):
+ super().__init__()
+ self.prompt_entered = False
+ self.input_required = True
+
+ def prompt(self, args=None):
+ self.prompt_entered = True
+ self.close()
+ # do not process input - it was processed here by user
+ return None
+
+
+class RefreshTestScreen(UIScreen):
+
+ def __init__(self):
+ super().__init__()
+ self.input_processed = False
+
+ def input(self, args, key):
+ self.input_processed = True
+ return key
+
+ def refresh(self, args=None):
+ super().refresh(args)
+ if self.input_processed:
+ self.close()
+ self.input_required = False
+ return
+
+
+class FailedSetupScreen(UIScreen):
+
+ def setup(self, args):
+ super().setup(args)
+ return False
+
+
+class NoSeparatorScreen(UIScreen):
+
+ def __init__(self, print_string):
+ super().__init__()
+ self.input_required = False
+ self.no_separator = True
+ self._print_string = print_string
+
+ def show_all(self):
+ print(self._print_string)
+ self.close()
+
+
+class NoInputScreen(UIScreen):
+
+ def __init__(self):
+ super().__init__()
+ self.input_required = False
+
+ def show_all(self):
+ super().show_all()
+ self.close()
+
+
+class InputScreen(UIScreen):
+
+ def __init__(self):
+ super().__init__()
+ self.input_processed = False
+
+ def input(self, args, key):
+ if key == "a":
+ self.input_processed = True
+ self.close()
+ return InputState.PROCESSED
+
+
+class InputStateCloseScreen(UIScreen):
+
+ def __init__(self):
+ super().__init__()
+ self.input_processed = False
+
+ def input(self, args, key):
+ self.input_processed = not self.input_processed
+ return InputState.PROCESSED_AND_CLOSE
+
+
+class InputStateRedrawScreen(UIScreen):
+
+ def __init__(self):
+ super().__init__()
+ self._refreshing = False
+ self.refreshed = False
+
+ def input(self, args, key):
+ if not self._refreshing:
+ self._refreshing = True
+ return InputState.PROCESSED_AND_REDRAW
+
+ self.refreshed = True
+ return InputState.PROCESSED_AND_CLOSE
+
+
+class ExceptionTestScreen(UIScreen):
+ """Raising an exception in some place of processing."""
+
+ REFRESH = 0
+ REDRAW = 1
+
+ def __init__(self, where):
+ super().__init__()
+ self._where = where
+ self.input_required = False
+
+ def refresh(self, args=None):
+ super().refresh()
+ if self._where == self.REFRESH:
+ raise TestRefreshException("Refresh test exception happened!")
+
+ def show_all(self):
+ super().show_all()
+ if self._where == self.REDRAW:
+ raise TestRedrawException("Redraw test exception happened!")
+
+
+class BlockingInputTestScreen(EmptyScreen):
+
+ def __init__(self, prompt_message, hidden):
+ super().__init__()
+ self._prompt_message = prompt_message
+ self._hidden = hidden
+ self.input_returned = None
+
+ def show_all(self):
+ self.input_returned = self.get_user_input(self._prompt_message, self._hidden)
+ super().show_all()
+
+
+class TestRefreshException(Exception):
+ pass
+
+
+class TestRedrawException(Exception):
+ pass
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/tests/units/main/scheduler_test.py b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/tests/units/main/scheduler_test.py
new file mode 100644
index 0000000..bd4837b
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/tests/units/main/scheduler_test.py
@@ -0,0 +1,181 @@
+# Screen scheduler test classes.
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+# Author(s): Jiri Konecny
+#
+
+import unittest
+
+from unittest import mock
+
+from simpleline.event_loop.main_loop import MainLoop
+from simpleline.render.screen import UIScreen
+from simpleline.render.screen_scheduler import ScreenScheduler
+from simpleline.render.screen_stack import ScreenStack, ScreenStackEmptyException
+
+
+class Scheduler_TestCase(unittest.TestCase):
+
+ def setUp(self):
+ self.stack = None
+ self.scheduler = None
+
+ def create_scheduler_with_stack(self):
+ self.stack = ScreenStack()
+ self.scheduler = ScreenScheduler(event_loop=mock.MagicMock(), scheduler_stack=self.stack)
+
+ def pop_last_item(self, remove=True):
+ return self.stack.pop(remove)
+
+ def test_create_scheduler(self):
+ scheduler = ScreenScheduler(MainLoop())
+ self.assertTrue(isinstance(scheduler._screen_stack, ScreenStack)) # pylint: disable=protected-access
+
+ def test_scheduler_quit_screen(self):
+ def test_callback():
+ pass
+ scheduler = ScreenScheduler(MainLoop())
+ self.assertEqual(scheduler.quit_screen, None)
+ scheduler.quit_screen = test_callback
+ self.assertEqual(scheduler.quit_screen, test_callback)
+
+ def test_nothing_to_render(self):
+ self.create_scheduler_with_stack()
+
+ self.assertTrue(self.scheduler.nothing_to_render)
+ self.assertTrue(self.stack.empty())
+
+ self.scheduler.schedule_screen(UIScreen())
+ self.assertFalse(self.scheduler.nothing_to_render)
+ self.assertFalse(self.stack.empty())
+
+ def test_schedule_screen(self):
+ self.create_scheduler_with_stack()
+
+ screen = UIScreen()
+ self.scheduler.schedule_screen(screen)
+ test_screen = self.pop_last_item(False)
+ self.assertEqual(test_screen.ui_screen, screen)
+ self.assertEqual(test_screen.args, None) # empty field - no arguments
+ self.assertFalse(test_screen.execute_new_loop)
+
+ # Schedule another screen, new one will be added to the bottom of the stack
+ new_screen = UIScreen()
+ self.scheduler.schedule_screen(new_screen)
+ # Here should still be the old screen
+ self.assertEqual(self.pop_last_item().ui_screen, screen)
+ # After removing the first we would find the second screen
+ self.assertEqual(self.pop_last_item().ui_screen, new_screen)
+
+ def test_replace_screen_with_empty_stack(self):
+ self.create_scheduler_with_stack()
+
+ with self.assertRaises(ScreenStackEmptyException):
+ self.scheduler.replace_screen(UIScreen())
+
+ def test_replace_screen(self):
+ self.create_scheduler_with_stack()
+
+ old_screen = UIScreen()
+ screen = UIScreen()
+ self.scheduler.schedule_screen(old_screen)
+ self.scheduler.replace_screen(screen)
+ self.assertEqual(self.pop_last_item(False).ui_screen, screen)
+
+ new_screen = UIScreen()
+ self.scheduler.replace_screen(new_screen)
+ self.assertEqual(self.pop_last_item().ui_screen, new_screen)
+ # The old_screen was replaced so the stack is empty now
+ self.assertTrue(self.stack.empty())
+
+ def test_replace_screen_with_args(self):
+ self.create_scheduler_with_stack()
+
+ old_screen = UIScreen()
+ screen = UIScreen()
+ self.scheduler.schedule_screen(old_screen)
+ self.scheduler.replace_screen(screen, "test")
+ test_screen = self.pop_last_item()
+ self.assertEqual(test_screen.ui_screen, screen)
+ self.assertEqual(test_screen.args, "test")
+ # The old_screen was replaced so the stack is empty now
+ self.assertTrue(self.stack.empty())
+
+ def test_switch_screen_with_empty_stack(self):
+ self.create_scheduler_with_stack()
+
+ screen = UIScreen()
+ self.scheduler.push_screen(screen)
+ self.assertEqual(self.pop_last_item().ui_screen, screen)
+
+ def test_switch_screen(self):
+ self.create_scheduler_with_stack()
+
+ screen = UIScreen()
+ new_screen = UIScreen()
+
+ self.scheduler.schedule_screen(screen)
+ self.scheduler.push_screen(new_screen)
+
+ test_screen = self.pop_last_item()
+ self.assertEqual(test_screen.ui_screen, new_screen)
+ self.assertEqual(test_screen.args, None)
+ self.assertEqual(test_screen.execute_new_loop, False)
+
+ # We popped the new_screen so the old screen should stay here
+ self.assertEqual(self.pop_last_item().ui_screen, screen)
+ self.assertTrue(self.stack.empty())
+
+ def test_switch_screen_with_args(self):
+ self.create_scheduler_with_stack()
+
+ screen = UIScreen()
+ self.scheduler.push_screen(screen, args="test")
+ self.assertEqual(self.pop_last_item(False).ui_screen, screen)
+ self.assertEqual(self.pop_last_item().args, "test")
+
+ @mock.patch('simpleline.render.screen_scheduler.ScreenScheduler._draw_screen')
+ def test_switch_screen_modal_empty_stack(self, _):
+ self.create_scheduler_with_stack()
+
+ screen = UIScreen()
+ self.scheduler.push_screen_modal(screen)
+ self.assertEqual(self.pop_last_item().ui_screen, screen)
+
+ @mock.patch('simpleline.render.screen_scheduler.ScreenScheduler._draw_screen')
+ def test_switch_screen_modal(self, _):
+ self.create_scheduler_with_stack()
+
+ screen = UIScreen()
+ new_screen = UIScreen()
+ self.scheduler.schedule_screen(screen)
+ self.scheduler.push_screen_modal(new_screen)
+
+ test_screen = self.pop_last_item()
+ self.assertEqual(test_screen.ui_screen, new_screen)
+ self.assertEqual(test_screen.args, None)
+ self.assertEqual(test_screen.execute_new_loop, True)
+
+ @mock.patch('simpleline.render.screen_scheduler.ScreenScheduler._draw_screen')
+ def test_switch_screen_modal_with_args(self, _):
+ self.create_scheduler_with_stack()
+
+ screen = UIScreen()
+ self.scheduler.push_screen_modal(screen, args="test")
+ self.assertEqual(self.pop_last_item(False).ui_screen, screen)
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/tests/units/main/screen_scheduler_test.py b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/tests/units/main/screen_scheduler_test.py
new file mode 100644
index 0000000..b4f3c48
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/tests/units/main/screen_scheduler_test.py
@@ -0,0 +1,207 @@
+# Screen scheduling test classes.
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+
+import unittest
+
+from io import StringIO
+from unittest import mock
+
+from simpleline.render.screen import UIScreen
+from simpleline.render.screen_handler import ScreenHandler
+
+from .. import UtilityMixin
+
+
+@mock.patch('sys.stdout', new_callable=StringIO)
+class ScreenScheduler_TestCase(unittest.TestCase, UtilityMixin):
+
+ def test_replace_screen(self, _):
+ replace_screen = ShowedCounterScreen()
+ screen = ShowedCounterScreen(replace_screen=replace_screen)
+
+ self.schedule_screen_and_run(screen)
+
+ self.assertEqual(screen.counter, 1)
+ self.assertEqual(replace_screen.counter, 1)
+
+ def test_switch_screen(self, _):
+ switched_screen = ShowedCounterScreen()
+ screen = ShowedCounterScreen(switched_screen)
+
+ self.schedule_screen_and_run(screen)
+
+ self.assertEqual(screen.counter, 2)
+ self.assertEqual(switched_screen.counter, 1)
+
+ def test_switch_screen_modal_in_render(self, _):
+ modal_screen = ModalTestScreen()
+ screen = ModalTestScreen(modal_screen_render=modal_screen)
+
+ self.schedule_screen_and_run(screen)
+
+ self.assertEqual(screen.copied_modal_counter, ModalTestScreen.AFTER_MODAL_RENDER)
+ self.assertEqual(modal_screen.copied_modal_counter, ModalTestScreen.BEFORE_MODAL_RENDER)
+
+ def test_switch_screen_modal_in_refresh(self, _):
+ modal_screen = ModalTestScreen()
+ screen = ModalTestScreen(modal_screen_refresh=modal_screen)
+
+ self.schedule_screen_and_run(screen)
+
+ self.assertEqual(screen.copied_modal_counter, ModalTestScreen.AFTER_MODAL_REFRESH)
+ self.assertEqual(modal_screen.copied_modal_counter, ModalTestScreen.BEFORE_MODAL_REFRESH)
+
+ def test_switch_screen_modal_refresh_and_render(self, _):
+ modal_refresh = ModalTestScreen()
+ modal_render = ModalTestScreen()
+ screen = ModalTestScreen(modal_screen_refresh=modal_refresh,
+ modal_screen_render=modal_render)
+
+ self.schedule_screen_and_run(screen)
+
+ self.assertEqual(screen.copied_modal_counter, ModalTestScreen.AFTER_MODAL_RENDER)
+ self.assertEqual(modal_refresh.copied_modal_counter, ModalTestScreen.BEFORE_MODAL_REFRESH)
+ self.assertEqual(modal_render.copied_modal_counter, ModalTestScreen.BEFORE_MODAL_RENDER)
+
+ def test_switch_screen_modal_render_recursive(self, _):
+ modal_render_inner = ModalTestScreen()
+ modal_render_outer = ModalTestScreen(modal_screen_render=modal_render_inner)
+ screen = ModalTestScreen(modal_screen_render=modal_render_outer)
+
+ self.schedule_screen_and_run(screen)
+
+ self.assertEqual(screen.copied_modal_counter, ModalTestScreen.AFTER_MODAL_RENDER)
+ # outer modal screen has AFTER_MODAL_RENDER because it was set before by inner loop
+ self.assertEqual(modal_render_outer.copied_modal_counter,
+ ModalTestScreen.AFTER_MODAL_RENDER)
+ self.assertEqual(modal_render_inner.copied_modal_counter,
+ ModalTestScreen.BEFORE_MODAL_RENDER)
+
+ @mock.patch('simpleline.input.input_handler.InputHandlerRequest._get_input')
+ def test_switch_screen_modal_input_order(self, mock_input, mock_stdout):
+ modal_screen = InputAndDrawScreen("Modal")
+ parent_screen = EmitDrawThenCreateModal(modal_screen, msg="Parent")
+ mock_input.return_value = "c"
+ expected = ["Modal", # modal needs to be printed first
+ "Parent", # draw enqueued draw signal -- manually registered in refresh()
+ "Parent"] # draw because modal screen was closed
+
+ self.schedule_screen_and_run(parent_screen)
+
+ self.maxDiff = None
+ self.assertEqual(self.create_output_with_separators(expected), mock_stdout.getvalue())
+
+
+class ShowedCounterScreen(UIScreen):
+
+ def __init__(self, switch_to_screen=None, replace_screen=None):
+ super().__init__()
+ self._switch_to_screen = switch_to_screen
+ self._replace_screen = replace_screen
+ self.counter = 0
+ self.input_required = False
+
+ def show_all(self):
+ super().show_all()
+ self.counter += 1
+ if self._switch_to_screen is not None:
+ ScreenHandler.push_screen(self._switch_to_screen)
+ self._switch_to_screen = None
+ elif self._replace_screen is not None:
+ ScreenHandler.replace_screen(self._replace_screen)
+ self._replace_screen = None
+ else:
+ self.close()
+
+
+class ModalTestScreen(UIScreen):
+ """Test if the modal screen is really modal and stops the execution in a place where
+ we start the modal screen.
+
+ This class have checkpoints which increment class variable counter. This counter is
+ copied in the modal instance to the local variable self.copied_modal_counter.
+ In the end we should check if the instance modal counter have the correct value, which is
+ before the modal screen was started (1).
+ """
+
+ INIT = 0
+ BEFORE_MODAL_REFRESH = 1
+ AFTER_MODAL_REFRESH = 2
+ BEFORE_MODAL_RENDER = 3
+ AFTER_MODAL_RENDER = 4
+
+ modal_counter = 0
+
+ def __init__(self, modal_screen_render=None, modal_screen_refresh=None):
+ super().__init__()
+ self._modal_screen_render = modal_screen_render
+ self._modal_screen_refresh = modal_screen_refresh
+ self.copied_modal_counter = 0
+ self.input_required = False
+ ModalTestScreen.modal_counter = self.INIT
+
+ def refresh(self, args=None):
+ super().refresh(args)
+ if self._modal_screen_refresh is not None:
+ # Start a new modal screen
+ ModalTestScreen.modal_counter = self.BEFORE_MODAL_REFRESH
+ ScreenHandler.push_screen_modal(self._modal_screen_refresh)
+ ModalTestScreen.modal_counter = self.AFTER_MODAL_REFRESH
+
+ def show_all(self):
+ super().show_all()
+ if self._modal_screen_render is not None:
+ # Start new modal screen
+ ModalTestScreen.modal_counter = self.BEFORE_MODAL_RENDER
+ ScreenHandler.push_screen_modal(self._modal_screen_render)
+ ModalTestScreen.modal_counter = self.AFTER_MODAL_RENDER
+
+ self.copied_modal_counter = ModalTestScreen.modal_counter
+ self.close()
+
+
+class EmitDrawThenCreateModal(UIScreen):
+
+ def __init__(self, refresh_screen, msg):
+ super().__init__()
+ self._refresh_screen = refresh_screen
+ self.title = msg
+ self.input_required = False
+
+ def refresh(self, args=None):
+ super().refresh(args)
+ if self._refresh_screen:
+ self.redraw()
+ ScreenHandler.push_screen_modal(self._refresh_screen)
+ self._refresh_screen = None
+ else:
+ self.close()
+
+
+class InputAndDrawScreen(UIScreen):
+
+ def __init__(self, msg):
+ super().__init__()
+ self.title = msg
+ self.input_required = False
+
+ def refresh(self, args=None):
+ super().refresh(args)
+ self.close()
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/tests/units/main/screen_stack_test.py b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/tests/units/main/screen_stack_test.py
new file mode 100644
index 0000000..a5012f3
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/tests/units/main/screen_stack_test.py
@@ -0,0 +1,162 @@
+# Screen stack test classes.
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+# Author(s): Jiri Konecny
+#
+
+
+import unittest
+
+from simpleline.render.screen import UIScreen
+from simpleline.render.screen_stack import ScreenStack, ScreenData, ScreenStackEmptyException
+
+
+class ScreenStack_TestCase(unittest.TestCase):
+
+ def test_append_screen(self):
+ stack = ScreenStack()
+ stack.append(ScreenData(None))
+
+ def test_is_empty(self):
+ stack = ScreenStack()
+ self.assertTrue(stack.empty())
+ stack.append(ScreenData(None))
+ self.assertFalse(stack.empty())
+
+ def test_pop(self):
+ stack = ScreenStack()
+ with self.assertRaises(ScreenStackEmptyException):
+ stack.pop()
+
+ with self.assertRaises(ScreenStackEmptyException):
+ stack.pop(False)
+
+ # stack.pop(True) will remove the item
+ stack.append(ScreenData(None))
+ stack.pop(True)
+ with self.assertRaises(ScreenStackEmptyException):
+ stack.pop()
+
+ # stack.pop() should behave the same as stack.pop(True)
+ stack.append(ScreenData(None))
+ stack.pop()
+ with self.assertRaises(ScreenStackEmptyException):
+ stack.pop()
+
+ stack.append(ScreenData(None))
+ stack.pop(False)
+ stack.pop(True)
+
+ def test_add_first(self):
+ stack = ScreenStack()
+
+ screen_data = ScreenData(None)
+ stack.add_first(screen_data)
+ self.assertEqual(stack.pop(False), screen_data)
+
+ # Add new Screen data to the end
+ new_screen_data = ScreenData(None)
+ stack.add_first(new_screen_data)
+ # First the old screen data should be there
+ self.assertEqual(stack.pop(), screen_data)
+ # Second should be the new screen data
+ self.assertEqual(stack.pop(), new_screen_data)
+
+ def test_size(self):
+ stack = ScreenStack()
+ self.assertEqual(stack.size(), 0)
+
+ stack.append(ScreenData(None))
+ self.assertEqual(stack.size(), 1)
+
+ stack.append(ScreenData(None))
+ self.assertEqual(stack.size(), 2)
+
+ # Remove from stack
+ stack.pop()
+ self.assertEqual(stack.size(), 1)
+ stack.pop()
+ self.assertEqual(stack.size(), 0)
+
+ # Add first when stack has items
+ stack.append(ScreenData(None))
+ stack.append(ScreenData(None))
+ self.assertEqual(stack.size(), 2)
+ stack.add_first(ScreenData(None))
+ self.assertEqual(stack.size(), 3)
+
+ def test_stack_dump(self):
+ stack = ScreenStack()
+
+ stack.append(ScreenData(TestScreen1()))
+ stack.append(ScreenData(TestScreen2()))
+
+ dump = stack.dump_stack()
+ dump = dump.replace('\n', '')
+ self.assertRegex(dump, r"TestScreen2.*TestScreen1")
+
+
+class ScreenData_TestCase(unittest.TestCase):
+
+ def setUp(self):
+ self.ui_screen = None
+
+ def _prepare(self):
+ self.ui_screen = UIScreen()
+
+ def _screen_check(self, test_screen, ui_screen, args, execute_new_loop):
+ self._prepare()
+ self.assertEqual(test_screen.ui_screen, ui_screen)
+ self.assertEqual(test_screen.args, args)
+ self.assertEqual(test_screen.execute_new_loop, execute_new_loop)
+
+ def test_screen_data(self):
+ self._prepare()
+ screen = ScreenData(self.ui_screen)
+ self._screen_check(screen, self.ui_screen, None, False)
+
+ def test_screen_data_with_args(self):
+ self._prepare()
+ screen = ScreenData(ui_screen=self.ui_screen, args=1)
+ self._screen_check(screen, self.ui_screen, 1, False)
+
+ array = [2, "a"]
+ screen2 = ScreenData(ui_screen=self.ui_screen, args=array)
+ self._screen_check(screen2, self.ui_screen, array, False)
+
+ def test_screen_data_with_execute_loop(self):
+ self._prepare()
+ screen = ScreenData(self.ui_screen, execute_new_loop=True)
+ self._screen_check(screen, self.ui_screen, None, True)
+
+ screen2 = ScreenData(self.ui_screen, execute_new_loop=False)
+ self._screen_check(screen2, self.ui_screen, None, False)
+
+ def test_screen_data_with_args_and_execute_loop(self):
+ self._prepare()
+ screen = ScreenData(self.ui_screen, "test", True)
+ self._screen_check(screen, self.ui_screen, "test", True)
+
+
+class TestScreen1(UIScreen):
+ pass
+
+
+class TestScreen2(UIScreen):
+ pass
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/tests/units/main/signal_handler_test.py b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/tests/units/main/signal_handler_test.py
new file mode 100644
index 0000000..e03187c
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/tests/units/main/signal_handler_test.py
@@ -0,0 +1,113 @@
+# Signal handler test classes.
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+
+
+import unittest
+
+from unittest.mock import patch, MagicMock
+
+from simpleline import App
+from simpleline.event_loop.signals import RenderScreenSignal, AbstractSignal
+from simpleline.render.screen import UIScreen
+
+
+class SignalHandler_TestCase(unittest.TestCase):
+
+ def setUp(self):
+ self.callback_called = False
+ self.priority = 0
+
+ def _callback(self, signal, data):
+ self.callback_called = True
+ signal.test_attribute = True
+
+ def test_basic_connect(self):
+ connect_screen = UIScreen()
+
+ App.initialize(scheduler=MagicMock())
+ connect_screen.connect(TestSignal, self._callback)
+ App.get_event_loop().enqueue_signal(TestSignal(self))
+ App.get_event_loop().process_signals()
+
+ self.assertTrue(self.callback_called)
+
+ def test_create_signal(self):
+ connect_screen = UIScreen()
+
+ App.initialize(scheduler=MagicMock())
+ signal = connect_screen.create_signal(TestSignal, priority=20)
+
+ self.assertEqual(signal.priority, 20)
+ self.assertTrue(isinstance(signal, TestSignal))
+ # source is set by create_signal
+ self.assertEqual(signal.source, connect_screen)
+
+ def test_emit(self):
+ connect_screen = UIScreen()
+
+ App.initialize(scheduler=MagicMock())
+ connect_screen.connect(TestSignal, self._callback)
+ connect_screen.emit(TestSignal(self))
+ App.get_event_loop().process_signals()
+
+ self.assertTrue(self.callback_called)
+
+ @patch('sys.stdout')
+ def test_connect_react_on_rendering(self, _):
+ connect_test_screen = TestRenderConnectHandler()
+ screen2 = EmptyScreen()
+
+ App.initialize()
+ App.get_scheduler().schedule_screen(connect_test_screen)
+ App.get_scheduler().schedule_screen(screen2)
+ App.run()
+
+ self.assertTrue(connect_test_screen.callback_called)
+
+
+class TestRenderConnectHandler(UIScreen):
+
+ def __init__(self):
+ super().__init__()
+ self.callback_called = False
+ self.input_required = False
+
+ def refresh(self, args=None):
+ super().refresh(args)
+ self.connect(RenderScreenSignal, self._callback)
+ self.close()
+
+ def _callback(self, signal, args):
+ self.callback_called = True
+
+
+class EmptyScreen(UIScreen):
+
+ def __init__(self):
+ super().__init__()
+ self.input_required = False
+
+ def show_all(self):
+ super().show_all()
+ self.close()
+
+
+class TestSignal(AbstractSignal):
+ pass
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/tests/units/main/ticket_machine_test.py b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/tests/units/main/ticket_machine_test.py
new file mode 100644
index 0000000..7f45a22
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/tests/units/main/ticket_machine_test.py
@@ -0,0 +1,106 @@
+# Widgets test classes.
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+
+from unittest import TestCase
+
+from simpleline.event_loop.ticket_machine import TicketMachine
+
+
+class TicketMachine_TestCase(TestCase):
+
+ def setUp(self):
+ self._tickets = TicketMachine()
+
+ def test_take_ticket(self):
+ line_id = 0
+ t = self._tickets.take_ticket(line_id)
+ self.assertEqual(t, 0)
+ t2 = self._tickets.take_ticket(line_id)
+ self.assertNotEqual(t, t2)
+
+ def test_check_ticket(self):
+ line_id = 0
+ t = self._tickets.take_ticket(line_id)
+
+ self.assertFalse(self._tickets.check_ticket(line_id, t))
+
+ self._tickets.mark_line_to_go(line_id)
+
+ self.assertTrue(self._tickets.check_ticket(line_id, t))
+
+ def test_mark_multiple_tickets(self):
+ line_id = 0
+
+ t1 = self._tickets.take_ticket(line_id)
+ t2 = self._tickets.take_ticket(line_id)
+ t3 = self._tickets.take_ticket(line_id)
+ t4 = self._tickets.take_ticket(line_id)
+
+ self._tickets.mark_line_to_go(line_id)
+ self.assertTrue(self._tickets.check_ticket(line_id, t1))
+ self.assertTrue(self._tickets.check_ticket(line_id, t2))
+ self.assertTrue(self._tickets.check_ticket(line_id, t3))
+ self.assertTrue(self._tickets.check_ticket(line_id, t4))
+
+ def test_mark_one_of_lines(self):
+ line_id1 = "a"
+ line_id2 = "b"
+
+ t1 = self._tickets.take_ticket(line_id1)
+ t2 = self._tickets.take_ticket(line_id1)
+ t3 = self._tickets.take_ticket(line_id2)
+ t4 = self._tickets.take_ticket(line_id2)
+
+ self._tickets.mark_line_to_go(line_id1)
+
+ self.assertTrue(self._tickets.check_ticket(line_id1, t1))
+ self.assertTrue(self._tickets.check_ticket(line_id1, t2))
+ self.assertFalse(self._tickets.check_ticket(line_id2, t3))
+ self.assertFalse(self._tickets.check_ticket(line_id2, t4))
+
+ def text_check_re_using(self):
+ line_id = "a"
+
+ t1 = self._tickets.take_ticket(line_id)
+ t2 = self._tickets.take_ticket(line_id)
+ t3 = self._tickets.take_ticket(line_id)
+
+ self._tickets.mark_line_to_go(line_id)
+
+ self.assertTrue(self._tickets.check_ticket(line_id, t1))
+ self.assertTrue(self._tickets.check_ticket(line_id, t2))
+
+ # it needs to be False when you check it again
+ self.assertFalse(self._tickets.check_ticket(line_id, t1))
+ self.assertFalse(self._tickets.check_ticket(line_id, t2))
+
+ # take new ticket and mark the line again
+
+ t4 = self._tickets.take_ticket(line_id)
+
+ self._tickets.mark_line_to_go(line_id)
+
+ # old checked tickets should be invalid now
+ self.assertFalse(self._tickets.check_ticket(line_id, t1))
+ self.assertFalse(self._tickets.check_ticket(line_id, t2))
+ # old not checked ticket should work
+ self.assertTrue(self._tickets.check_ticket(line_id, t3))
+ # new tickets should work
+ self.assertTrue(self._tickets.check_ticket(line_id, t4))
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/tests/units/main/widgets_test.py b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/tests/units/main/widgets_test.py
new file mode 100644
index 0000000..fedb7c8
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/tests/units/main/widgets_test.py
@@ -0,0 +1,335 @@
+# Widgets test classes.
+#
+# This file is part of Simpleline Text UI library.
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Simpleline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Simpleline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Simpleline. If not, see .
+#
+
+import unittest
+
+from io import StringIO
+from unittest.mock import patch
+
+from simpleline import App
+from simpleline.render.prompt import Prompt
+from simpleline.render.screen import UIScreen
+from simpleline.render.widgets import TextWidget, SeparatorWidget, CheckboxWidget, CenterWidget, \
+ ColumnWidget, EntryWidget
+
+
+class BaseWidgets_TestCase(unittest.TestCase):
+ """Base class containing helper functions."""
+ def setUp(self):
+ self.w1 = TextWidget(u"Můj krásný dlouhý text")
+ self.w2 = TextWidget(u"Test")
+ self.w3 = TextWidget(u"Test 2")
+ self.w4 = TextWidget(u"Krásný dlouhý text podruhé")
+ self.w5 = TextWidget(u"Test 3")
+ self.w6 = TextWidget("The rescue environment will now attempt "
+ "to find your Linux installation and mount it under "
+ "the directory : bla. You can then make any changes "
+ "required to your system. Choose '1' to proceed with "
+ "this step.\nYou can choose to mount your file "
+ "systems read-only instead of read-write by choosing "
+ "'2'.\nIf for some reason this process does not work "
+ "choose '3' to skip directly to a shell.\n\n")
+ self.w7 = TextWidget("Wrapping toooooooooooooooooooooooooooooooooooooooooooo"
+ "oooooooooooooooooooooooooooooooooooooooooooooooooooooo long word.")
+ self.w8 = TextWidget("Text that would be wrapped exactly at the screen width should"
+ " have special test. This one.")
+
+ def evaluate_result(self, test_result, expected_result):
+ self.assertEqual(len(test_result), len(expected_result))
+ for i in range(0, len(test_result)): # pylint: disable=consider-using-enumerate
+ self.assertEqual(test_result[i], expected_result[i])
+
+
+class Widgets_TestCase(BaseWidgets_TestCase):
+
+ def test_separator_widget(self):
+ w = SeparatorWidget()
+ w.render(80)
+
+ res_lines = w.get_lines()
+
+ expected_result = [u""]
+
+ self.evaluate_result(res_lines, expected_result)
+
+ def test_separator_widget_multiline(self):
+ w = SeparatorWidget(3)
+ w.render(80)
+
+ res_lines = w.get_lines()
+
+ expected_result = [u"",
+ u"",
+ u""]
+
+ self.evaluate_result(res_lines, expected_result)
+
+ def test_column_widget(self):
+ # Test column text
+ c = ColumnWidget([(20, [self.w1, self.w2, self.w3]),
+ (25, [self.w4, self.w5]),
+ (15, [self.w1, self.w2, self.w3])], spacing=3)
+ c.render(80)
+ res_lines = c.get_lines()
+
+ expected_result = [u"Můj krásný dlouhý Krásný dlouhý text Můj krásný",
+ u"text podruhé dlouhý text",
+ u"Test Test 3 Test",
+ u"Test 2 Test 2"]
+ self.evaluate_result(res_lines, expected_result)
+
+ def test_column_wrapping(self):
+ # Test column wrapping text
+ c = ColumnWidget([(15, [self.w1, self.w2, self.w3]), (10, [self.w4, self.w5])], spacing=1)
+ c.render(80)
+
+ expected_result = [u"Můj krásný Krásný",
+ u"dlouhý text dlouhý",
+ u"Test text",
+ u"Test 2 podruhé",
+ u" Test 3"]
+
+ res_lines = c.get_lines()
+ self.evaluate_result(res_lines, expected_result)
+
+ def test_multiline_text(self):
+ self.w6.render(80)
+ expected_result = [
+ "The rescue environment will now attempt to find your Linux installation and",
+ "mount it under the directory : bla. You can then make any changes required to",
+ "your system. Choose '1' to proceed with this step.",
+ "You can choose to mount your file systems read-only instead of read-write by",
+ "choosing '2'.",
+ "If for some reason this process does not work choose '3' to skip directly to a",
+ "shell.",
+ "",
+ ""]
+ res_lines = self.w6.get_lines()
+ self.evaluate_result(res_lines, expected_result)
+
+ def test_wrapping(self):
+ # wrap long text
+ self.w7.render(80)
+ expected_result = [
+ "Wrapping toooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo",
+ "oooooooooooooooooooooooooooo long word."]
+ res_lines = self.w7.get_lines()
+
+ self.assertEqual(len(res_lines), len(expected_result))
+ for i in range(0, len(res_lines)): # pylint: disable=consider-using-enumerate
+ self.assertEqual(res_lines[i], expected_result[i])
+
+ self.w8.render(80)
+ # pylint: disable=line-too-long
+ expected_result = ["Text that would be wrapped exactly at the screen width should have special test.",
+ "This one."]
+ res_lines = self.w8.get_lines()
+
+ self.evaluate_result(res_lines, expected_result)
+
+ def test_checkbox(self):
+ checkbox = CheckboxWidget(title="Test Title", text="Description")
+
+ checkbox.render(80)
+
+ expected_result = [u"[ ] Test Title",
+ u" (Description)"]
+
+ self.evaluate_result(checkbox.get_lines(), expected_result)
+
+ def test_completed_checkbox(self):
+ checkbox = CheckboxWidget(title="Title", text="Description", completed=True)
+
+ checkbox.render(80)
+
+ expected_result = [u"[x] Title",
+ u" (Description)"]
+
+ self.evaluate_result(checkbox.get_lines(), expected_result)
+
+ def test_key_checkbox(self):
+ checkbox = CheckboxWidget(key="o", title="Title", text="Description", completed=True)
+
+ checkbox.render(80)
+
+ expected_result = [u"[o] Title",
+ u" (Description)"]
+
+ self.evaluate_result(checkbox.get_lines(), expected_result)
+
+ def test_empty_checkbox(self):
+ checkbox = CheckboxWidget()
+
+ checkbox.render(80)
+
+ expected_result = [u"[ ]"]
+
+ self.evaluate_result(checkbox.get_lines(), expected_result)
+
+ def test_checkbox_wrapping(self):
+ checkbox = CheckboxWidget(title="Title", text="Testing\nwrapping")
+
+ checkbox.render(80)
+
+ expected_result = [u"[ ] Title",
+ u" (Testing",
+ u" wrapping)"]
+
+ self.evaluate_result(checkbox.get_lines(), expected_result)
+
+ def test_center_widget(self):
+ w = CenterWidget(self.w2)
+
+ w.render(10)
+
+ expected_result = [u" Test"]
+
+ self.evaluate_result(w.get_lines(), expected_result)
+
+ def test_entry_widget(self):
+ title = "Title"
+ value = "Value"
+ w = EntryWidget(title=title, value=value)
+
+ w.render(30)
+
+ expected_result = [title,
+ value]
+
+ self.evaluate_result(w.get_lines(), expected_result)
+
+ def test_entry_too_long(self):
+ title = "Title too long"
+ value = "Value also too long"
+ w = EntryWidget(title=title, value=value)
+
+ w.render(10)
+
+ expected_result = [u"Title too",
+ u"long",
+ u"Value also",
+ u"too long"]
+
+ self.evaluate_result(w.get_lines(), expected_result)
+
+ def test_entry_value_empty(self):
+ title = "Title"
+ w = EntryWidget(title=title)
+
+ w.render(20)
+
+ expected_result = [title]
+
+ self.evaluate_result(w.get_lines(), expected_result=expected_result)
+
+
+@patch('simpleline.input.input_handler.InputHandlerRequest._get_input')
+@patch('sys.stdout', new_callable=StringIO)
+class WidgetProcessing_TestCase(unittest.TestCase):
+
+ @staticmethod
+ def _calculate_spacer():
+ # this calculation is taken from scheduler for default width '80'
+ return '\n'.join(2 * [80 * '='])
+
+ def _expected_output(self, text, widget_height=20):
+
+ # two lines are always added to the printed size
+ prompt_height = 2
+ real_widget_height = widget_height - prompt_height
+
+ lines = text.split('\n')
+
+ # add Press ENTER... to the text
+ if len(lines) - 1 >= real_widget_height:
+ lines.insert(real_widget_height, "\nPress %s to continue: \n" % Prompt.ENTER)
+
+ msg = self._calculate_spacer() + '\n'
+ msg += "\n".join(lines)
+ msg += "\n"
+ return msg
+
+ def test_draw_simple_widget(self, out_mock, in_mock):
+ widget_text = "Test"
+ screen = ScreenWithWidget(widget_text)
+
+ App.initialize()
+ App.get_scheduler().schedule_screen(screen)
+ App.run()
+
+ self.assertEqual(self._expected_output(widget_text), out_mock.getvalue())
+
+ def test_widget_multiline(self, out_mock, in_mock):
+ widget_text = "Testing output\n\n\nAgain..."
+ screen = ScreenWithWidget(widget_text)
+
+ App.initialize()
+ App.get_scheduler().schedule_screen(screen)
+ App.run()
+
+ self.assertEqual(self._expected_output(widget_text), out_mock.getvalue())
+
+ def test_widget_too_high(self, out_mock, in_mock):
+ in_mock.return_value = "\n"
+ in_mock.side_effect = lambda: print('\n')
+
+ widget_text = ("Line\n"
+ "Line2\n"
+ "Line3\n"
+ "Line4\n"
+ "Line5")
+ # Screen height take into account also 2 lines for prompt
+ screen = ScreenWithWidget(widget_text, height=6)
+
+ App.initialize()
+ App.get_scheduler().schedule_screen(screen)
+ App.run()
+
+ self.assertEqual(self._expected_output(widget_text, widget_height=6), out_mock.getvalue())
+
+ def test_widget_is_exactly_height_to_print(self, out_mock, in_mock):
+ widget_text = ("Line\n"
+ "Line2\n"
+ "Line3\n"
+ "Line4")
+ # Screen height take into account also 2 lines for prompt
+ screen = ScreenWithWidget(widget_text, height=6)
+
+ App.initialize()
+ App.get_scheduler().schedule_screen(screen)
+ App.run()
+
+ self.assertEqual(self._expected_output(widget_text, widget_height=6), out_mock.getvalue())
+
+
+class ScreenWithWidget(UIScreen):
+
+ def __init__(self, msg, height=25):
+ super().__init__(screen_height=height)
+ self._msg = msg
+ self.input_required = False
+
+ def refresh(self, args=None):
+ super().refresh(args)
+ self.window.add(TextWidget(self._msg))
+
+ def show_all(self):
+ super().show_all()
+ self.close()
diff --git a/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/tests/units/run_test.sh b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/tests/units/run_test.sh
new file mode 100755
index 0000000..029a8a6
--- /dev/null
+++ b/pkgbuilds/python-simpleline/src/python-simpleline-simpleline-1.8/tests/units/run_test.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+if [ -z $PYTHON ]; then
+ PYTHON=python3
+fi
+
+SCRIPT_DIR=$(dirname $0)
+
+$PYTHON -m unittest discover -v -s $SCRIPT_DIR/../ -p '*_test.py'
diff --git a/setup-aur.sh b/setup-aur.sh
index 10409f2..c421318 100755
--- a/setup-aur.sh
+++ b/setup-aur.sh
@@ -160,6 +160,24 @@ for pkg in "${AUR_PACKAGES[@]}"; do
success "$pkg build OK"
done
+# ------------------------------------------------------------
+# PKGBUILD locaux FWS (deps Anaconda packagées maison, pkgbuilds/)
+# cf. portage de l'installateur Anaconda sur Arch.
+# ------------------------------------------------------------
+LOCAL_PKGBUILDS_DIR="$SCRIPT_DIR/pkgbuilds"
+if [ -d "$LOCAL_PKGBUILDS_DIR" ]; then
+ for dir in "$LOCAL_PKGBUILDS_DIR"/*/; do
+ [ -f "$dir/PKGBUILD" ] || continue
+ name="$(basename "$dir")"
+ info "Build local $name…"
+ cp -a "$dir" "$BUILD_DIR/$name"
+ chown -R "$BUILD_USER:$BUILD_USER" "$BUILD_DIR/$name"
+ su - "$BUILD_USER" -c "cd $BUILD_DIR/$name && makepkg -sf --noconfirm" \
+ || error "Build local $name échoué"
+ success "$name build OK"
+ done
+fi
+
# ============================================================
step "4/4 — Création du repo pacman local fws-local"
# ============================================================
@@ -171,6 +189,16 @@ for pkg in "${AUR_PACKAGES[@]}"; do
|| error "Paquet $pkg introuvable dans $BUILD_DIR/$pkg"
done
+# PKGBUILD locaux FWS
+if [ -d "$LOCAL_PKGBUILDS_DIR" ]; then
+ for dir in "$LOCAL_PKGBUILDS_DIR"/*/; do
+ [ -f "$dir/PKGBUILD" ] || continue
+ name="$(basename "$dir")"
+ cp "$BUILD_DIR/$name"/*.pkg.tar.zst "$LOCAL_REPO/" \
+ || error "Paquet local $name introuvable"
+ done
+fi
+
repo-add "$LOCAL_REPO/fws-local.db.tar.gz" "$LOCAL_REPO"/*.pkg.tar.zst \
|| error "repo-add a échoué"
success "Repo local prêt : $LOCAL_REPO"