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 @@ + + + + + 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/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 @@ +[![Copr build status](https://copr.fedorainfracloud.org/coprs/g/rhinstaller/Anaconda/package/python-simpleline/status_image/last_build.png)](https://copr.fedorainfracloud.org/coprs/g/rhinstaller/Anaconda/package/python-simpleline/) +[![Translation status](https://translate.fedoraproject.org/widgets/python-simpleline/-/master/svg-badge.svg)](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"