feat(Anaconda): Local Repo
This commit is contained in:
@@ -26,11 +26,4 @@ file_permissions=(
|
|||||||
["/usr/local/bin/choose-mirror"]="0:0:755"
|
["/usr/local/bin/choose-mirror"]="0:0:755"
|
||||||
["/usr/local/bin/Installation_guide"]="0:0:755"
|
["/usr/local/bin/Installation_guide"]="0:0:755"
|
||||||
["/usr/local/bin/livecd-sound"]="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"
|
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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"
|
||||||
|
}
|
||||||
@@ -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
|
||||||
Binary file not shown.
@@ -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
|
||||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
# Author(s): Chris Lumens <clumens@redhat.com>
|
||||||
|
#
|
||||||
|
|
||||||
|
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"])
|
||||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
# Author(s): Matt Wilson <msw@redhat.com>
|
||||||
|
# Erik Troan <ewt@redhat.com>
|
||||||
|
# Chris Lumens <clumens@redhat.com>
|
||||||
|
#
|
||||||
|
# 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)
|
||||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
# Author(s): Chris Lumens <clumens@redhat.com>
|
||||||
|
#
|
||||||
|
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)
|
||||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
# Author(s): David Cantrell <dcantrell@redhat.com>
|
||||||
|
#
|
||||||
|
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
|
||||||
+71
@@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
# Author: Vratislav Podzimek <vpodzime@redhat.com>
|
||||||
|
#
|
||||||
|
#
|
||||||
|
|
||||||
|
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
|
||||||
+181
@@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
# Author(s): Chris Lumens <clumens@redhat.com>
|
||||||
|
#
|
||||||
|
|
||||||
|
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
|
||||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
# Author(s): Chris Lumens <clumens@redhat.com>
|
||||||
|
#
|
||||||
|
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)
|
||||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
# Author(s): Chris Lumens <clumens@redhat.com>
|
||||||
|
# Vratislav Podzimek <vpodzime@redhat.com>
|
||||||
|
#
|
||||||
|
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)
|
||||||
@@ -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.
|
||||||
|
|
||||||
|
<one line to give the program's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) 19yy <name of author>
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
<signature of Ty Coon>, 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.
|
||||||
@@ -0,0 +1,248 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<interface>
|
||||||
|
<!-- interface-requires gtk+ 3.0 -->
|
||||||
|
<object class="GtkWindow" id="exceptionWindow">
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="window_position">center-always</property>
|
||||||
|
<property name="default_width">600</property>
|
||||||
|
<property name="type_hint">dialog</property>
|
||||||
|
<property name="urgency_hint">True</property>
|
||||||
|
<signal name="delete-event" handler="on_main_window_deleted" swapped="no"/>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox" id="mainBox">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="orientation">vertical</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox" id="box1">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="halign">start</property>
|
||||||
|
<property name="valign">center</property>
|
||||||
|
<property name="margin_left">20</property>
|
||||||
|
<property name="margin_top">30</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkImage" id="image1">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="stock">gtk-dialog-warning</property>
|
||||||
|
<property name="icon-size">5</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="padding">7</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="titleLabel">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="label" translatable="yes">An unknown error has occurred</property>
|
||||||
|
<attributes>
|
||||||
|
<attribute name="weight" value="bold"/>
|
||||||
|
<attribute name="scale" value="1.1000000000000001"/>
|
||||||
|
</attributes>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">False</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="explainLabel">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="halign">start</property>
|
||||||
|
<property name="valign">start</property>
|
||||||
|
<property name="margin_left">40</property>
|
||||||
|
<property name="margin_right">40</property>
|
||||||
|
<property name="label" translatable="yes">This program has encountered an unknown error. You may
|
||||||
|
report the bug below or quit the program.</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="padding">21</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkButtonBox" id="buttonbox1">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="valign">start</property>
|
||||||
|
<property name="margin_right">20</property>
|
||||||
|
<property name="spacing">10</property>
|
||||||
|
<property name="layout_style">end</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkButton" id="reportButton">
|
||||||
|
<property name="label" translatable="yes">_Report Bug</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="receives_default">True</property>
|
||||||
|
<property name="use_underline">True</property>
|
||||||
|
<signal name="clicked" handler="on_report_clicked" swapped="no"/>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkButton" id="quitButton">
|
||||||
|
<property name="label" translatable="yes">_Quit</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="receives_default">True</property>
|
||||||
|
<property name="use_underline">True</property>
|
||||||
|
<signal name="clicked" handler="on_quit_clicked" swapped="no"/>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">2</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkExpander" id="expander">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="margin_left">10</property>
|
||||||
|
<property name="margin_right">10</property>
|
||||||
|
<property name="margin_bottom">10</property>
|
||||||
|
<property name="use_underline">True</property>
|
||||||
|
<signal name="activate" handler="on_expander_activated" swapped="no"/>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox" id="box2">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="vexpand">True</property>
|
||||||
|
<property name="orientation">vertical</property>
|
||||||
|
<property name="spacing">6</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="commentLabel">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="halign">start</property>
|
||||||
|
<property name="margin_left">2</property>
|
||||||
|
<property name="xalign">0.10000000149011612</property>
|
||||||
|
<property name="label" translatable="yes">The output below may help determine the cause of the error:</property>
|
||||||
|
<attributes>
|
||||||
|
<attribute name="style" value="italic"/>
|
||||||
|
</attributes>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkScrolledWindow" id="tracebackScrolledWindow">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="vexpand">True</property>
|
||||||
|
<property name="shadow_type">in</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkTextView" id="tracebackView">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="vscroll_policy">natural</property>
|
||||||
|
<property name="editable">False</property>
|
||||||
|
<property name="cursor_visible">False</property>
|
||||||
|
<property name="buffer">tracebackBuffer</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">True</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox" id="bottomBox">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="spacing">6</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="warningLabel">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="xalign">1</property>
|
||||||
|
<property name="label" translatable="yes">'Debug' may take you to tty1.</property>
|
||||||
|
<attributes>
|
||||||
|
<attribute name="style" value="italic"/>
|
||||||
|
<attribute name="scale" value="0.80000000000000004"/>
|
||||||
|
</attributes>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">True</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkButton" id="debugButton">
|
||||||
|
<property name="label" translatable="yes">_Debug</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="receives_default">True</property>
|
||||||
|
<property name="use_underline">True</property>
|
||||||
|
<signal name="clicked" handler="on_debug_clicked" swapped="no"/>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">2</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child type="label">
|
||||||
|
<object class="GtkLabel" id="expanderLabel">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="label" translatable="yes">More _info...</property>
|
||||||
|
<property name="use_underline">True</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">3</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<object class="GtkTextBuffer" id="tracebackBuffer">
|
||||||
|
<property name="text" translatable="yes">No additional info available.</property>
|
||||||
|
</object>
|
||||||
|
</interface>
|
||||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1 @@
|
|||||||
|
/home/nocode/Files/FWS-ISO/pkgbuilds/python-meh/python-meh-0.43.tar.gz
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
*.pyc
|
||||||
|
.*.swp
|
||||||
|
|
||||||
|
python-meh*.tar.gz
|
||||||
|
|
||||||
|
ChangeLog
|
||||||
|
|
||||||
|
po/*.gmo
|
||||||
|
po/*.po
|
||||||
|
po/POTFILES
|
||||||
|
|
||||||
|
build/
|
||||||
|
|
||||||
|
.directory
|
||||||
@@ -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.
|
||||||
|
|
||||||
|
<one line to give the program's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) 19yy <name of author>
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
<signature of Ty Coon>, 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.
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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"])
|
||||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
# Author(s): Chris Lumens <clumens@redhat.com>
|
||||||
|
#
|
||||||
|
|
||||||
|
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"])
|
||||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
# Author(s): Matt Wilson <msw@redhat.com>
|
||||||
|
# Erik Troan <ewt@redhat.com>
|
||||||
|
# Chris Lumens <clumens@redhat.com>
|
||||||
|
#
|
||||||
|
# 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)
|
||||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
# Author(s): Chris Lumens <clumens@redhat.com>
|
||||||
|
#
|
||||||
|
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)
|
||||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
# Author(s): David Cantrell <dcantrell@redhat.com>
|
||||||
|
#
|
||||||
|
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
|
||||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
# Author: Vratislav Podzimek <vpodzime@redhat.com>
|
||||||
|
#
|
||||||
|
#
|
||||||
|
|
||||||
|
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
|
||||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
# Author(s): Chris Lumens <clumens@redhat.com>
|
||||||
|
#
|
||||||
|
|
||||||
|
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
|
||||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
# Author(s): Chris Lumens <clumens@redhat.com>
|
||||||
|
#
|
||||||
|
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)
|
||||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
# Author(s): Chris Lumens <clumens@redhat.com>
|
||||||
|
# Vratislav Podzimek <vpodzime@redhat.com>
|
||||||
|
#
|
||||||
|
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)
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 3.8 KiB |
@@ -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
|
||||||
|
|
||||||
|
|
||||||
@@ -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 <EMAIL@ADDRESS>, 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 <EMAIL@ADDRESS>\n"
|
||||||
|
"Language-Team: LANGUAGE <LL@li.org>\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 ""
|
||||||
@@ -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 <mkolman@redhat.com> - 0.43-1
|
||||||
|
- Set translations domain for glade files (vtrefny)
|
||||||
|
|
||||||
|
* Tue Sep 29 2015 Martin Kolman <mkolman@redhat.com> - 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 <mkolman@redhat.com> - 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 <mkolman@redhat.com> - 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 <mkolman@redhat.com> - 0.39-1
|
||||||
|
- Handle LANG=C (mkolman)
|
||||||
|
|
||||||
|
* Tue Mar 31 2015 Martin Kolman <mkolman@redhat.com> - 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 <mkolman@redhat.com> - 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 <mkolman@redhat.com> - 0.36-1
|
||||||
|
- Package's epoch returned by RPM is an integer (#1199263) (vpodzime)
|
||||||
|
|
||||||
|
* Tue Feb 17 2015 Martin Kolman <mkolman@redhat.com> - 0.35-1
|
||||||
|
- Add option to remove the 'Debug' option/button for mainExceptionWindow (vtrefny)
|
||||||
|
|
||||||
|
* Tue Dec 09 2014 Vratislav Podzimek <vpodzime@redhat.com> - 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 <vpodzime@redhat.com> - 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 <mkolman@redhat.com> - 0.32-1
|
||||||
|
- Translation update
|
||||||
|
|
||||||
|
* Fri Mar 21 2014 Martin Kolman <mkolman@redhat.com> - 0.31-1
|
||||||
|
- Translation update
|
||||||
|
|
||||||
|
* Wed Dec 18 2013 Vratislav Podzimek <vpodzime@redhat.com> - 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 <vpodzime@redhat.com> - 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 <vpodzime@redhat.com> - 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 <vpodzime@redhat.com> - 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 <rel-eng@lists.fedoraproject.org> - 0.26-2
|
||||||
|
- Rebuilt for https://fedoraproject.org/wiki/Fedora_20_Mass_Rebuild
|
||||||
|
|
||||||
|
* Tue Jul 23 2013 Vratislav Podzimek <vpodzime@redhat.com> - 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 <vpodzime@redhat.com> - 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 <vpodzime@redhat.com> - 0.24-1
|
||||||
|
- Epoch of the package from RPM db can be None (#957789) (vpodzime)
|
||||||
|
|
||||||
|
* Wed Apr 17 2013 Vratislav Podzimek <vpodzime@redhat.com> - 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 <vpodzime@redhat.com> - 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 <rel-eng@lists.fedoraproject.org> - 0.21-2
|
||||||
|
- Rebuilt for https://fedoraproject.org/wiki/Fedora_19_Mass_Rebuild
|
||||||
|
|
||||||
|
* Mon Jan 21 2013 Vratislav Podzimek <vpodzime@redhat.com> - 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 <vpodzime@redhat.com> - 0.20-1
|
||||||
|
- Handle non-ascii dict keys and values correctly (#883641) (vpodzime)
|
||||||
|
|
||||||
|
* Wed Nov 14 2012 Vratislav Podzimek <vpodzime@redhat.com> - 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 <vpodzime@redhat.com> - 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 <vpodzime@redhat.com> - 0.17-1
|
||||||
|
- Handle unicode strings correctly (#854959) (vpodzime)
|
||||||
|
|
||||||
|
* Tue Sep 11 2012 Vratislav Podzimek <vpodzime@redhat.com> - 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 <vpodzime@redhat.com> - 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 <vpodzime@redhat.com> - 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 <vpodzime@redhat.com> - 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 <rel-eng@lists.fedoraproject.org> - 0.12-2
|
||||||
|
- Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild
|
||||||
|
|
||||||
|
* Tue Dec 20 2011 Vratislav Podzimek <vpodzime@redhat.com> 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 <rel-eng@lists.fedoraproject.org> - 0.11-2
|
||||||
|
- Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild
|
||||||
|
|
||||||
|
* Tue Jan 25 2011 Chris Lumens <clumens@redhat.com> - 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 <clumens@redhat.com> 0.10-1
|
||||||
|
- Treat classes like simple types, too. (clumens)
|
||||||
|
|
||||||
|
* Thu Jun 10 2010 Chris Lumens <clumens@redhat.com> - 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 <clumens@redhat.com> - 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 <clumens@redhat.com> - 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 <clumens@redhat.com> - 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 <clumens@redhat.com> - 0.5-1
|
||||||
|
- Always compare version numbers as strings (#526188).
|
||||||
|
|
||||||
|
* Fri Sep 25 2009 Chris Lumens <clumens@redhat.com> - 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 <clumens@redhat.com> - 0.3-1
|
||||||
|
- Pull in lots of new translations (#522410).
|
||||||
|
|
||||||
|
* Wed Aug 19 2009 Chris Lumens <clumens@redhat.com> - 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 <clumens@redhat.com> - 0.1-1
|
||||||
|
- Initial package.
|
||||||
@@ -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'])
|
||||||
@@ -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()
|
||||||
@@ -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")
|
||||||
@@ -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)
|
||||||
|
|
||||||
|
|
||||||
@@ -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)
|
||||||
@@ -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)
|
||||||
|
|
||||||
@@ -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)
|
||||||
@@ -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("<safe_string_test.TestClass2 object at ", self.safestr)
|
||||||
|
self.assertIn(str(self.enc_unistr), self.safestr)
|
||||||
|
self.assertIn(self.unistr, self.safestr)
|
||||||
|
|
||||||
|
self.assertIn("\\xff\\xff\\xfe", self.safestr)
|
||||||
|
self.assertIn(str(self.test_object), self.safestr)
|
||||||
|
|
||||||
@@ -0,0 +1,248 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<interface>
|
||||||
|
<!-- interface-requires gtk+ 3.0 -->
|
||||||
|
<object class="GtkWindow" id="exceptionWindow">
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="window_position">center-always</property>
|
||||||
|
<property name="default_width">600</property>
|
||||||
|
<property name="type_hint">dialog</property>
|
||||||
|
<property name="urgency_hint">True</property>
|
||||||
|
<signal name="delete-event" handler="on_main_window_deleted" swapped="no"/>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox" id="mainBox">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="orientation">vertical</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox" id="box1">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="halign">start</property>
|
||||||
|
<property name="valign">center</property>
|
||||||
|
<property name="margin_left">20</property>
|
||||||
|
<property name="margin_top">30</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkImage" id="image1">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="stock">gtk-dialog-warning</property>
|
||||||
|
<property name="icon-size">5</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="padding">7</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="titleLabel">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="label" translatable="yes">An unknown error has occurred</property>
|
||||||
|
<attributes>
|
||||||
|
<attribute name="weight" value="bold"/>
|
||||||
|
<attribute name="scale" value="1.1000000000000001"/>
|
||||||
|
</attributes>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">False</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="explainLabel">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="halign">start</property>
|
||||||
|
<property name="valign">start</property>
|
||||||
|
<property name="margin_left">40</property>
|
||||||
|
<property name="margin_right">40</property>
|
||||||
|
<property name="label" translatable="yes">This program has encountered an unknown error. You may
|
||||||
|
report the bug below or quit the program.</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="padding">21</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkButtonBox" id="buttonbox1">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="valign">start</property>
|
||||||
|
<property name="margin_right">20</property>
|
||||||
|
<property name="spacing">10</property>
|
||||||
|
<property name="layout_style">end</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkButton" id="reportButton">
|
||||||
|
<property name="label" translatable="yes">_Report Bug</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="receives_default">True</property>
|
||||||
|
<property name="use_underline">True</property>
|
||||||
|
<signal name="clicked" handler="on_report_clicked" swapped="no"/>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkButton" id="quitButton">
|
||||||
|
<property name="label" translatable="yes">_Quit</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="receives_default">True</property>
|
||||||
|
<property name="use_underline">True</property>
|
||||||
|
<signal name="clicked" handler="on_quit_clicked" swapped="no"/>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">2</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkExpander" id="expander">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="margin_left">10</property>
|
||||||
|
<property name="margin_right">10</property>
|
||||||
|
<property name="margin_bottom">10</property>
|
||||||
|
<property name="use_underline">True</property>
|
||||||
|
<signal name="activate" handler="on_expander_activated" swapped="no"/>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox" id="box2">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="vexpand">True</property>
|
||||||
|
<property name="orientation">vertical</property>
|
||||||
|
<property name="spacing">6</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="commentLabel">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="halign">start</property>
|
||||||
|
<property name="margin_left">2</property>
|
||||||
|
<property name="xalign">0.10000000149011612</property>
|
||||||
|
<property name="label" translatable="yes">The output below may help determine the cause of the error:</property>
|
||||||
|
<attributes>
|
||||||
|
<attribute name="style" value="italic"/>
|
||||||
|
</attributes>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkScrolledWindow" id="tracebackScrolledWindow">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="vexpand">True</property>
|
||||||
|
<property name="shadow_type">in</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkTextView" id="tracebackView">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="vscroll_policy">natural</property>
|
||||||
|
<property name="editable">False</property>
|
||||||
|
<property name="cursor_visible">False</property>
|
||||||
|
<property name="buffer">tracebackBuffer</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">True</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox" id="bottomBox">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="spacing">6</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="warningLabel">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="xalign">1</property>
|
||||||
|
<property name="label" translatable="yes">'Debug' may take you to tty1.</property>
|
||||||
|
<attributes>
|
||||||
|
<attribute name="style" value="italic"/>
|
||||||
|
<attribute name="scale" value="0.80000000000000004"/>
|
||||||
|
</attributes>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">True</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkButton" id="debugButton">
|
||||||
|
<property name="label" translatable="yes">_Debug</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="receives_default">True</property>
|
||||||
|
<property name="use_underline">True</property>
|
||||||
|
<signal name="clicked" handler="on_debug_clicked" swapped="no"/>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">2</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child type="label">
|
||||||
|
<object class="GtkLabel" id="expanderLabel">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="label" translatable="yes">More _info...</property>
|
||||||
|
<property name="use_underline">True</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">3</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<object class="GtkTextBuffer" id="tracebackBuffer">
|
||||||
|
<property name="text" translatable="yes">No additional info available.</property>
|
||||||
|
</object>
|
||||||
|
</interface>
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
|
<config xmlns="http://zanata.org/namespace/config/">
|
||||||
|
<url>https://fedora.zanata.org/</url>
|
||||||
|
<project>python-meh</project>
|
||||||
|
<project-version>master</project-version>
|
||||||
|
<project-type>gettext</project-type>
|
||||||
|
|
||||||
|
<locales>
|
||||||
|
<locale>sq</locale>
|
||||||
|
<locale>ar</locale>
|
||||||
|
<locale>as</locale>
|
||||||
|
<locale>ast</locale>
|
||||||
|
<locale>bal</locale>
|
||||||
|
<locale>eu</locale>
|
||||||
|
<locale>bn</locale>
|
||||||
|
<locale>bn-IN</locale>
|
||||||
|
<locale>brx</locale>
|
||||||
|
<locale>bs</locale>
|
||||||
|
<locale>br</locale>
|
||||||
|
<locale>bg</locale>
|
||||||
|
<locale>ca</locale>
|
||||||
|
<locale>zh-CN</locale>
|
||||||
|
<locale>zh-HK</locale>
|
||||||
|
<locale>zh-TW</locale>
|
||||||
|
<locale>kw</locale>
|
||||||
|
<locale>kw-GB</locale>
|
||||||
|
<locale>cs</locale>
|
||||||
|
<locale>da</locale>
|
||||||
|
<locale>nl</locale>
|
||||||
|
<locale>en-GB</locale>
|
||||||
|
<locale>eo</locale>
|
||||||
|
<locale>et</locale>
|
||||||
|
<locale>fi</locale>
|
||||||
|
<locale>fr</locale>
|
||||||
|
<locale>gl</locale>
|
||||||
|
<locale>ka</locale>
|
||||||
|
<locale>de</locale>
|
||||||
|
<locale>el</locale>
|
||||||
|
<locale>gu</locale>
|
||||||
|
<locale>he</locale>
|
||||||
|
<locale>hi</locale>
|
||||||
|
<locale>hu</locale>
|
||||||
|
<locale>is</locale>
|
||||||
|
<locale>id</locale>
|
||||||
|
<locale>ia</locale>
|
||||||
|
<locale>it</locale>
|
||||||
|
<locale>ja</locale>
|
||||||
|
<locale>kn</locale>
|
||||||
|
<locale>kk</locale>
|
||||||
|
<locale>km</locale>
|
||||||
|
<locale>ky</locale>
|
||||||
|
<locale>ko</locale>
|
||||||
|
<locale>lt</locale>
|
||||||
|
<locale>nds</locale>
|
||||||
|
<locale>mk</locale>
|
||||||
|
<locale>mai</locale>
|
||||||
|
<locale>ms</locale>
|
||||||
|
<locale>ml</locale>
|
||||||
|
<locale>mr</locale>
|
||||||
|
<locale>mn</locale>
|
||||||
|
<locale>ne</locale>
|
||||||
|
<locale>nb</locale>
|
||||||
|
<locale>nn</locale>
|
||||||
|
<locale>or</locale>
|
||||||
|
<locale>pa</locale>
|
||||||
|
<locale>fa</locale>
|
||||||
|
<locale>pl</locale>
|
||||||
|
<locale>pt</locale>
|
||||||
|
<locale>pt-BR</locale>
|
||||||
|
<locale>ro</locale>
|
||||||
|
<locale>ru</locale>
|
||||||
|
<locale>sr</locale>
|
||||||
|
<locale>sr@latin</locale>
|
||||||
|
<locale>si</locale>
|
||||||
|
<locale>sk</locale>
|
||||||
|
<locale>sl</locale>
|
||||||
|
<locale>es</locale>
|
||||||
|
<locale>sv</locale>
|
||||||
|
<locale>tg</locale>
|
||||||
|
<locale>ta</locale>
|
||||||
|
<locale>te</locale>
|
||||||
|
<locale>bo</locale>
|
||||||
|
<locale>tr</locale>
|
||||||
|
<locale>uk</locale>
|
||||||
|
<locale>ur</locale>
|
||||||
|
<locale>wba</locale>
|
||||||
|
<locale>cy</locale>
|
||||||
|
<locale>lv</locale>
|
||||||
|
<locale>kw@uccor</locale>
|
||||||
|
<locale>kw@kkcor</locale>
|
||||||
|
<locale>af</locale>
|
||||||
|
<locale>am</locale>
|
||||||
|
<locale>be</locale>
|
||||||
|
<locale>hr</locale>
|
||||||
|
<locale>de-CH</locale>
|
||||||
|
<locale>th</locale>
|
||||||
|
<locale>vi</locale>
|
||||||
|
<locale>zu</locale>
|
||||||
|
<locale>ilo</locale>
|
||||||
|
<locale>nso</locale>
|
||||||
|
<locale>tw</locale>
|
||||||
|
<locale>yo</locale>
|
||||||
|
<locale>anp</locale>
|
||||||
|
</locales>
|
||||||
|
|
||||||
|
</config>
|
||||||
@@ -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"
|
||||||
|
}
|
||||||
@@ -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
|
||||||
Binary file not shown.
@@ -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
|
||||||
+136
@@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
|
||||||
|
__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()
|
||||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
+31
@@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
# 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."""
|
||||||
+264
@@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
|
||||||
|
# This class can be overridden to use any existing event loop of your program.
|
||||||
|
#
|
||||||
|
# Author(s): Jiri Konecny <jkonecny@redhat.com>
|
||||||
|
#
|
||||||
|
|
||||||
|
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
|
||||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
+153
@@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
# Author(s): Jiri Konecny <jkonecny@redhat.com>
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
+230
@@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
# Author(s): Jiri Konecny <jkonecny@redhat.com>
|
||||||
|
#
|
||||||
|
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()
|
||||||
+247
@@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
# Author(s): Jiri Konecny <jkonecny@redhat.com>
|
||||||
|
#
|
||||||
|
|
||||||
|
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)
|
||||||
+110
@@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
# Author(s): Jiri Konecny <jkonecny@redhat.com>
|
||||||
|
#
|
||||||
|
|
||||||
|
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."""
|
||||||
+81
@@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
# Author(s): Jiri Konecny <jkonecny@redhat.com>
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
+112
@@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
|
||||||
|
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
|
||||||
+22
@@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
|
||||||
|
from simpleline.input.input_handler import InputHandler, PasswordInputHandler
|
||||||
|
|
||||||
|
__all__ = ["InputHandler", "PasswordInputHandler"]
|
||||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
+292
@@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
|
||||||
|
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)
|
||||||
+238
@@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
|
||||||
|
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 ""
|
||||||
+39
@@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
# Author(s): Jiri Konecny <jkonecny@redhat.com>
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
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)
|
||||||
+31
@@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
# Author(s): Jiri Konecny <jkonecny@redhat.com>
|
||||||
|
#
|
||||||
|
|
||||||
|
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."""
|
||||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
+253
@@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
|
||||||
|
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
|
||||||
+475
@@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
|
||||||
|
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
|
||||||
+154
@@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
# Author(s): Vendula Poncova <vponcova@redhat.com>
|
||||||
|
#
|
||||||
|
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) + ": "
|
||||||
+313
@@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
# Author(s): Jiri Konecny <jkonecny@redhat.com>
|
||||||
|
#
|
||||||
|
|
||||||
|
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
|
||||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user