Sun Feb 2 15:35:05 UTC 2025 I: starting to build errbot/trixie/armhf on jenkins on '2025-02-02 15:34' Sun Feb 2 15:35:05 UTC 2025 I: The jenkins build log is/was available at https://jenkins.debian.net/userContent/reproducible/debian/build_service/armhf_14/9990/console.log Sun Feb 2 15:35:05 UTC 2025 I: Downloading source for trixie/errbot=6.2.0+ds-4 --2025-02-02 15:35:05-- http://deb.debian.org/debian/pool/main/e/errbot/errbot_6.2.0%2bds-4.dsc Connecting to 46.16.76.132:3128... connected. Proxy request sent, awaiting response... 200 OK Length: 2233 (2.2K) [text/prs.lines.tag] Saving to: ‘errbot_6.2.0+ds-4.dsc’ 0K .. 100% 389M=0s 2025-02-02 15:35:05 (389 MB/s) - ‘errbot_6.2.0+ds-4.dsc’ saved [2233/2233] Sun Feb 2 15:35:06 UTC 2025 I: errbot_6.2.0+ds-4.dsc -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA512 Format: 3.0 (quilt) Source: errbot Binary: errbot Architecture: all Version: 6.2.0+ds-4 Maintainer: Debian Python Team Uploaders: Birger Schacht Homepage: http://errbot.io Standards-Version: 4.5.1 Vcs-Browser: https://salsa.debian.org/python-team/packages/errbot Vcs-Git: https://salsa.debian.org/python-team/packages/errbot.git Build-Depends: debhelper-compat (= 13), dh-sequence-python3, git-core, localehelper, python3-all, python3-ansi, python3-bottle, python3-colorlog, python3-daemonize, python3-flask, python3-jinja2, python3-markdown, python3-openssl, python3-pygments, python3-pytest, python3-requests, python3-setuptools, python3-webtest, python3-yapsy, python3-dulwich Package-List: errbot deb net optional arch=all Checksums-Sha1: b23eaa6a5b0e436861b433da2aededefa754985c 1341096 errbot_6.2.0+ds.orig.tar.xz 671fe4358d85bd1bdd6472259fb36f410f654fb5 7412 errbot_6.2.0+ds-4.debian.tar.xz Checksums-Sha256: 5a9bd3ce367906518960493f56e2c547047435c02581d4e3cef5c5f6255cb18a 1341096 errbot_6.2.0+ds.orig.tar.xz be45ae630c90777d33da6b3d986488f103e11add0edd99f6ff4247e10cfd3074 7412 errbot_6.2.0+ds-4.debian.tar.xz Files: 8c91bbd9f785227ef8a1747e464fa86e 1341096 errbot_6.2.0+ds.orig.tar.xz 7b3d51bb8099646560ee85ef0264b860 7412 errbot_6.2.0+ds-4.debian.tar.xz -----BEGIN PGP SIGNATURE----- iQJFBAEBCgAvFiEEj23hBDd/OxHnQXSHMfMURUShdBoFAmeYDGgRHHRjaGV0QGRl Ymlhbi5vcmcACgkQMfMURUShdBpl1RAAkimq/urVrSs0L6HP0k8D6sULn9gL8zIb q+b47y2bDTUoa4Lgomfc9LR3WrQFhUz083W0eE6S+1/qCz7pePHCLN8qk6iiZwrb J/l0PTPsaVCKq1Rsq/S/ObQzc4QfBu+UgErVjQHpp5FrTEucDHYfTaWEvKbYHly9 TshAlKcLLPY9SZ86BU05emnKHK4vzSxD70Ehxi/BrE6joSh2WU+hm1OGPy738y9p PdEEyYjfN3dyMGtarqStVX3P81t/QRmKYW9fY1rTvg6eaSoIgPVKKrOw2NhGZ/z8 OOkuEWus2S6AVemBY1wZOZjJNlEJaXQFBnS+MIUaLbRww976IGU9Qh+OtpHoOxdW j5gO0hLO3iNWiD4kE9k8enSPKQpQ4D2cgvQAqP56AGVb6+ZyG/60gxKP4TCClgFQ ykylgEuKRqe1lXTaenvw5ht/j/z/4O4AbtCY8KfSESTfcfK4hY6q9ZgbIJaB34GO aQdyLqBT1wa1hmX43TXAgS/vRgY2ZIahAb7/8u2Cn32ZKlQDvD5qiaKGchay6iFu yK1gHTCHVrTc9NmP2i9h4gOf0SViD7IgdCe/o4AI+JgaPiUdMwddCwqB11/FFUm9 mmCAWIxlbo8dVjst+aEr0yukzVsIfZef4txZ6h5g8CS+NSef2F8yUDSkYZc5aFoM uIlsRpfFl0o= =e8nH -----END PGP SIGNATURE----- Sun Feb 2 15:35:06 UTC 2025 I: Checking whether the package is not for us Sun Feb 2 15:35:06 UTC 2025 I: Starting 1st build on remote node virt32z-armhf-rb.debian.net. Sun Feb 2 15:35:06 UTC 2025 I: Preparing to do remote build '1' on virt32z-armhf-rb.debian.net. Sun Feb 2 15:38:15 UTC 2025 I: Deleting $TMPDIR on virt32z-armhf-rb.debian.net. I: pbuilder: network access will be disabled during build I: Current time: Sun Feb 2 03:35:11 -12 2025 I: pbuilder-time-stamp: 1738510511 I: Building the build Environment I: extracting base tarball [/var/cache/pbuilder/trixie-reproducible-base.tgz] I: copying local configuration W: --override-config is not set; not updating apt.conf Read the manpage for details. I: mounting /proc filesystem I: mounting /sys filesystem I: creating /{dev,run}/shm I: mounting /dev/pts filesystem I: redirecting /dev/ptmx to /dev/pts/ptmx I: policy-rc.d already exists I: Copying source file I: copying [errbot_6.2.0+ds-4.dsc] I: copying [./errbot_6.2.0+ds.orig.tar.xz] I: copying [./errbot_6.2.0+ds-4.debian.tar.xz] I: Extracting source dpkg-source: warning: cannot verify inline signature for ./errbot_6.2.0+ds-4.dsc: unsupported subcommand dpkg-source: info: extracting errbot in errbot-6.2.0+ds dpkg-source: info: unpacking errbot_6.2.0+ds.orig.tar.xz dpkg-source: info: unpacking errbot_6.2.0+ds-4.debian.tar.xz dpkg-source: info: using patch list from debian/patches/series dpkg-source: info: applying 0002-Remove-pygments-markdown-lexer-dependency.patch dpkg-source: info: applying SyntaxWarning.patch I: Not using root during the build. I: Installing the build-deps I: user script /srv/workspace/pbuilder/24190/tmp/hooks/D02_print_environment starting I: set BUILDDIR='/build/reproducible-path' BUILDUSERGECOS='first user,first room,first work-phone,first home-phone,first other' BUILDUSERNAME='pbuilder1' BUILD_ARCH='armhf' DEBIAN_FRONTEND='noninteractive' DEB_BUILD_OPTIONS='buildinfo=+all reproducible=+all parallel=3 ' DISTRIBUTION='trixie' HOME='/root' HOST_ARCH='armhf' IFS=' ' INVOCATION_ID='8c5c1f7259db4f0199985f35df7c5c70' LANG='C' LANGUAGE='en_US:en' LC_ALL='C' MAIL='/var/mail/root' OPTIND='1' PATH='/usr/sbin:/usr/bin:/sbin:/bin:/usr/games' PBCURRENTCOMMANDLINEOPERATION='build' PBUILDER_OPERATION='build' PBUILDER_PKGDATADIR='/usr/share/pbuilder' PBUILDER_PKGLIBDIR='/usr/lib/pbuilder' PBUILDER_SYSCONFDIR='/etc' PPID='24190' PS1='# ' PS2='> ' PS4='+ ' PWD='/' SHELL='/bin/bash' SHLVL='2' SUDO_COMMAND='/usr/bin/timeout -k 18.1h 18h /usr/bin/ionice -c 3 /usr/bin/nice /usr/sbin/pbuilder --build --configfile /srv/reproducible-results/rbuild-debian/r-b-build.jeO5yNpS/pbuilderrc_APP4 --distribution trixie --hookdir /etc/pbuilder/first-build-hooks --debbuildopts -b --basetgz /var/cache/pbuilder/trixie-reproducible-base.tgz --buildresult /srv/reproducible-results/rbuild-debian/r-b-build.jeO5yNpS/b1 --logfile b1/build.log errbot_6.2.0+ds-4.dsc' SUDO_GID='110' SUDO_UID='103' SUDO_USER='jenkins' TERM='unknown' TZ='/usr/share/zoneinfo/Etc/GMT+12' USER='root' _='/usr/bin/systemd-run' http_proxy='http://10.0.0.15:3142/' I: uname -a Linux virt32z 6.1.0-30-armmp-lpae #1 SMP Debian 6.1.124-1 (2025-01-12) armv7l GNU/Linux I: ls -l /bin lrwxrwxrwx 1 root root 7 Nov 22 14:40 /bin -> usr/bin I: user script /srv/workspace/pbuilder/24190/tmp/hooks/D02_print_environment finished -> Attempting to satisfy build-dependencies -> Creating pbuilder-satisfydepends-dummy package Package: pbuilder-satisfydepends-dummy Version: 0.invalid.0 Architecture: armhf Maintainer: Debian Pbuilder Team Description: Dummy package to satisfy dependencies with aptitude - created by pbuilder This package was created automatically by pbuilder to satisfy the build-dependencies of the package being currently built. Depends: debhelper-compat (= 13), dh-sequence-python3, git-core, localehelper, python3-all, python3-ansi, python3-bottle, python3-colorlog, python3-daemonize, python3-flask, python3-jinja2, python3-markdown, python3-openssl, python3-pygments, python3-pytest, python3-requests, python3-setuptools, python3-webtest, python3-yapsy, python3-dulwich dpkg-deb: building package 'pbuilder-satisfydepends-dummy' in '/tmp/satisfydepends-aptitude/pbuilder-satisfydepends-dummy.deb'. Selecting previously unselected package pbuilder-satisfydepends-dummy. (Reading database ... 19573 files and directories currently installed.) Preparing to unpack .../pbuilder-satisfydepends-dummy.deb ... Unpacking pbuilder-satisfydepends-dummy (0.invalid.0) ... dpkg: pbuilder-satisfydepends-dummy: dependency problems, but configuring anyway as you requested: pbuilder-satisfydepends-dummy depends on debhelper-compat (= 13); however: Package debhelper-compat is not installed. pbuilder-satisfydepends-dummy depends on dh-sequence-python3; however: Package dh-sequence-python3 is not installed. pbuilder-satisfydepends-dummy depends on git-core; however: Package git-core is not installed. pbuilder-satisfydepends-dummy depends on localehelper; however: Package localehelper is not installed. pbuilder-satisfydepends-dummy depends on python3-all; however: Package python3-all is not installed. pbuilder-satisfydepends-dummy depends on python3-ansi; however: Package python3-ansi is not installed. pbuilder-satisfydepends-dummy depends on python3-bottle; however: Package python3-bottle is not installed. pbuilder-satisfydepends-dummy depends on python3-colorlog; however: Package python3-colorlog is not installed. pbuilder-satisfydepends-dummy depends on python3-daemonize; however: Package python3-daemonize is not installed. pbuilder-satisfydepends-dummy depends on python3-flask; however: Package python3-flask is not installed. pbuilder-satisfydepends-dummy depends on python3-jinja2; however: Package python3-jinja2 is not installed. pbuilder-satisfydepends-dummy depends on python3-markdown; however: Package python3-markdown is not installed. pbuilder-satisfydepends-dummy depends on python3-openssl; however: Package python3-openssl is not installed. pbuilder-satisfydepends-dummy depends on python3-pygments; however: Package python3-pygments is not installed. pbuilder-satisfydepends-dummy depends on python3-pytest; however: Package python3-pytest is not installed. pbuilder-satisfydepends-dummy depends on python3-requests; however: Package python3-requests is not installed. pbuilder-satisfydepends-dummy depends on python3-setuptools; however: Package python3-setuptools is not installed. pbuilder-satisfydepends-dummy depends on python3-webtest; however: Package python3-webtest is not installed. pbuilder-satisfydepends-dummy depends on python3-yapsy; however: Package python3-yapsy is not installed. pbuilder-satisfydepends-dummy depends on python3-dulwich; however: Package python3-dulwich is not installed. Setting up pbuilder-satisfydepends-dummy (0.invalid.0) ... Reading package lists... Building dependency tree... Reading state information... Initializing package states... Writing extended state information... Building tag database... pbuilder-satisfydepends-dummy is already installed at the requested version (0.invalid.0) pbuilder-satisfydepends-dummy is already installed at the requested version (0.invalid.0) The following NEW packages will be installed: autoconf{a} automake{a} autopoint{a} autotools-dev{a} bsdextrautils{a} ca-certificates{a} debhelper{a} dh-autoreconf{a} dh-python{a} dh-strip-nondeterminism{a} dwz{a} file{a} gettext{a} gettext-base{a} git{a} git-man{a} groff-base{a} intltool-debian{a} libarchive-zip-perl{a} libbrotli1{a} libcom-err2{a} libcurl3t64-gnutls{a} libdebhelper-perl{a} libelf1t64{a} liberror-perl{a} libexpat1{a} libffi8{a} libfile-stripnondeterminism-perl{a} libgnutls30t64{a} libgssapi-krb5-2{a} libicu72{a} libidn2-0{a} libk5crypto3{a} libkeyutils1{a} libkrb5-3{a} libkrb5support0{a} libldap2{a} libmagic-mgc{a} libmagic1t64{a} libnghttp2-14{a} libnghttp3-9{a} libngtcp2-16{a} libngtcp2-crypto-gnutls8{a} libnsl2{a} libp11-kit0{a} libpipeline1{a} libpsl5t64{a} libpython3-stdlib{a} libpython3.12-minimal{a} libpython3.12-stdlib{a} libpython3.13-minimal{a} libpython3.13-stdlib{a} libreadline8t64{a} librtmp1{a} libsasl2-2{a} libsasl2-modules-db{a} libssh2-1t64{a} libtasn1-6{a} libtirpc-common{a} libtirpc3t64{a} libtool{a} libuchardet0{a} libunistring5{a} libxml2{a} localehelper{a} locales{a} m4{a} man-db{a} media-types{a} netbase{a} openssl{a} po-debconf{a} python3{a} python3-all{a} python3-ansi{a} python3-autocommand{a} python3-bcrypt{a} python3-blinker{a} python3-bottle{a} python3-bs4{a} python3-certifi{a} python3-cffi-backend{a} python3-chardet{a} python3-charset-normalizer{a} python3-click{a} python3-colorlog{a} python3-cryptography{a} python3-daemonize{a} python3-dulwich{a} python3-flask{a} python3-idna{a} python3-inflect{a} python3-iniconfig{a} python3-itsdangerous{a} python3-jaraco.context{a} python3-jaraco.functools{a} python3-jaraco.text{a} python3-jinja2{a} python3-legacy-cgi{a} python3-markdown{a} python3-markupsafe{a} python3-minimal{a} python3-more-itertools{a} python3-openssl{a} python3-packaging{a} python3-paste{a} python3-pastedeploy{a} python3-pastedeploy-tpl{a} python3-pkg-resources{a} python3-pluggy{a} python3-pygments{a} python3-pytest{a} python3-requests{a} python3-setuptools{a} python3-soupsieve{a} python3-tempita{a} python3-typeguard{a} python3-typing-extensions{a} python3-urllib3{a} python3-waitress{a} python3-webob{a} python3-webtest{a} python3-werkzeug{a} python3-yapsy{a} python3-zipp{a} python3.12{a} python3.12-minimal{a} python3.13{a} python3.13-minimal{a} readline-common{a} sensible-utils{a} tzdata{a} The following packages are RECOMMENDED but will NOT be installed: curl krb5-locales less libarchive-cpio-perl libldap-common libltdl-dev libmail-sendmail-perl libsasl2-modules lynx openssh-client publicsuffix python3-asgiref python3-babel python3-dotenv python3-fastimport python3-lxml python3-pastescript python3-pyinotify python3-yaml wget 0 packages upgraded, 132 newly installed, 0 to remove and 0 not upgraded. Need to get 54.8 MB of archives. After unpacking 218 MB will be used. Writing extended state information... Get: 1 http://deb.debian.org/debian trixie/main armhf libpython3.13-minimal armhf 3.13.1-3+b1 [845 kB] Get: 2 http://deb.debian.org/debian trixie/main armhf libexpat1 armhf 2.6.4-1 [83.5 kB] Get: 3 http://deb.debian.org/debian trixie/main armhf python3.13-minimal armhf 3.13.1-3+b1 [1838 kB] Get: 4 http://deb.debian.org/debian trixie/main armhf python3-minimal armhf 3.13.1-2 [27.0 kB] Get: 5 http://deb.debian.org/debian trixie/main armhf media-types all 10.1.0 [26.9 kB] Get: 6 http://deb.debian.org/debian trixie/main armhf netbase all 6.4 [12.8 kB] Get: 7 http://deb.debian.org/debian trixie/main armhf tzdata all 2024b-6 [257 kB] Get: 8 http://deb.debian.org/debian trixie/main armhf libffi8 armhf 3.4.6-1 [20.0 kB] Get: 9 http://deb.debian.org/debian trixie/main armhf readline-common all 8.2-6 [69.4 kB] Get: 10 http://deb.debian.org/debian trixie/main armhf libreadline8t64 armhf 8.2-6 [146 kB] Get: 11 http://deb.debian.org/debian trixie/main armhf libpython3.13-stdlib armhf 3.13.1-3+b1 [1850 kB] Get: 12 http://deb.debian.org/debian trixie/main armhf python3.13 armhf 3.13.1-3+b1 [740 kB] Get: 13 http://deb.debian.org/debian trixie/main armhf libpython3-stdlib armhf 3.13.1-2 [9952 B] Get: 14 http://deb.debian.org/debian trixie/main armhf python3 armhf 3.13.1-2 [28.0 kB] Get: 15 http://deb.debian.org/debian trixie/main armhf libpython3.12-minimal armhf 3.12.8-5+b1 [803 kB] Get: 16 http://deb.debian.org/debian trixie/main armhf python3.12-minimal armhf 3.12.8-5+b1 [1799 kB] Get: 17 http://deb.debian.org/debian trixie/main armhf sensible-utils all 0.0.24 [24.8 kB] Get: 18 http://deb.debian.org/debian trixie/main armhf openssl armhf 3.4.0-2 [1388 kB] Get: 19 http://deb.debian.org/debian trixie/main armhf ca-certificates all 20241223 [164 kB] Get: 20 http://deb.debian.org/debian trixie/main armhf libmagic-mgc armhf 1:5.45-3+b1 [314 kB] Get: 21 http://deb.debian.org/debian trixie/main armhf libmagic1t64 armhf 1:5.45-3+b1 [98.5 kB] Get: 22 http://deb.debian.org/debian trixie/main armhf file armhf 1:5.45-3+b1 [42.3 kB] Get: 23 http://deb.debian.org/debian trixie/main armhf gettext-base armhf 0.23.1-1 [238 kB] Get: 24 http://deb.debian.org/debian trixie/main armhf libuchardet0 armhf 0.0.8-1+b2 [65.6 kB] Get: 25 http://deb.debian.org/debian trixie/main armhf groff-base armhf 1.23.0-7 [1095 kB] Get: 26 http://deb.debian.org/debian trixie/main armhf locales all 2.40-6 [3901 kB] Get: 27 http://deb.debian.org/debian trixie/main armhf bsdextrautils armhf 2.40.4-2 [84.6 kB] Get: 28 http://deb.debian.org/debian trixie/main armhf libpipeline1 armhf 1.5.8-1 [35.0 kB] Get: 29 http://deb.debian.org/debian trixie/main armhf man-db armhf 2.13.0-1 [1382 kB] Get: 30 http://deb.debian.org/debian trixie/main armhf m4 armhf 1.4.19-5 [272 kB] Get: 31 http://deb.debian.org/debian trixie/main armhf autoconf all 2.72-3 [493 kB] Get: 32 http://deb.debian.org/debian trixie/main armhf autotools-dev all 20220109.1 [51.6 kB] Get: 33 http://deb.debian.org/debian trixie/main armhf automake all 1:1.16.5-1.3 [823 kB] Get: 34 http://deb.debian.org/debian trixie/main armhf autopoint all 0.23.1-1 [770 kB] Get: 35 http://deb.debian.org/debian trixie/main armhf libdebhelper-perl all 13.24.1 [90.9 kB] Get: 36 http://deb.debian.org/debian trixie/main armhf libtool all 2.5.4-2 [539 kB] Get: 37 http://deb.debian.org/debian trixie/main armhf dh-autoreconf all 20 [17.1 kB] Get: 38 http://deb.debian.org/debian trixie/main armhf libarchive-zip-perl all 1.68-1 [104 kB] Get: 39 http://deb.debian.org/debian trixie/main armhf libfile-stripnondeterminism-perl all 1.14.1-1 [19.6 kB] Get: 40 http://deb.debian.org/debian trixie/main armhf dh-strip-nondeterminism all 1.14.1-1 [8584 B] Get: 41 http://deb.debian.org/debian trixie/main armhf libelf1t64 armhf 0.192-4 [184 kB] Get: 42 http://deb.debian.org/debian trixie/main armhf dwz armhf 0.15-1+b2 [106 kB] Get: 43 http://deb.debian.org/debian trixie/main armhf libunistring5 armhf 1.3-1 [444 kB] Get: 44 http://deb.debian.org/debian trixie/main armhf libicu72 armhf 72.1-6 [9086 kB] Get: 45 http://deb.debian.org/debian trixie/main armhf libxml2 armhf 2.12.7+dfsg+really2.9.14-0.2+b1 [605 kB] Get: 46 http://deb.debian.org/debian trixie/main armhf gettext armhf 0.23.1-1 [1560 kB] Get: 47 http://deb.debian.org/debian trixie/main armhf intltool-debian all 0.35.0+20060710.6 [22.9 kB] Get: 48 http://deb.debian.org/debian trixie/main armhf po-debconf all 1.0.21+nmu1 [248 kB] Get: 49 http://deb.debian.org/debian trixie/main armhf debhelper all 13.24.1 [920 kB] Get: 50 http://deb.debian.org/debian trixie/main armhf python3-autocommand all 2.2.2-3 [13.6 kB] Get: 51 http://deb.debian.org/debian trixie/main armhf python3-more-itertools all 10.6.0-1 [65.3 kB] Get: 52 http://deb.debian.org/debian trixie/main armhf python3-typing-extensions all 4.12.2-2 [73.0 kB] Get: 53 http://deb.debian.org/debian trixie/main armhf python3-typeguard all 4.4.1-1 [37.0 kB] Get: 54 http://deb.debian.org/debian trixie/main armhf python3-inflect all 7.3.1-2 [32.4 kB] Get: 55 http://deb.debian.org/debian trixie/main armhf python3-jaraco.context all 6.0.0-1 [7984 B] Get: 56 http://deb.debian.org/debian trixie/main armhf python3-jaraco.functools all 4.1.0-1 [12.0 kB] Get: 57 http://deb.debian.org/debian trixie/main armhf python3-pkg-resources all 75.6.0-1 [222 kB] Get: 58 http://deb.debian.org/debian trixie/main armhf python3-jaraco.text all 4.0.0-1 [11.4 kB] Get: 59 http://deb.debian.org/debian trixie/main armhf python3-zipp all 3.21.0-1 [10.6 kB] Get: 60 http://deb.debian.org/debian trixie/main armhf python3-setuptools all 75.6.0-1 [720 kB] Get: 61 http://deb.debian.org/debian trixie/main armhf dh-python all 6.20250108 [113 kB] Get: 62 http://deb.debian.org/debian trixie/main armhf libbrotli1 armhf 1.1.0-2+b6 [282 kB] Get: 63 http://deb.debian.org/debian trixie/main armhf libidn2-0 armhf 2.3.7-2+b1 [125 kB] Get: 64 http://deb.debian.org/debian trixie/main armhf libp11-kit0 armhf 0.25.5-3 [385 kB] Get: 65 http://deb.debian.org/debian trixie/main armhf libtasn1-6 armhf 4.19.0-3+b3 [43.9 kB] Get: 66 http://deb.debian.org/debian trixie/main armhf libgnutls30t64 armhf 3.8.8-2 [1370 kB] Get: 67 http://deb.debian.org/debian trixie/main armhf libkrb5support0 armhf 1.21.3-4 [30.3 kB] Get: 68 http://deb.debian.org/debian trixie/main armhf libcom-err2 armhf 1.47.2-1 [23.3 kB] Get: 69 http://deb.debian.org/debian trixie/main armhf libk5crypto3 armhf 1.21.3-4 [75.8 kB] Get: 70 http://deb.debian.org/debian trixie/main armhf libkeyutils1 armhf 1.6.3-4 [8096 B] Get: 71 http://deb.debian.org/debian trixie/main armhf libkrb5-3 armhf 1.21.3-4 [285 kB] Get: 72 http://deb.debian.org/debian trixie/main armhf libgssapi-krb5-2 armhf 1.21.3-4 [116 kB] Get: 73 http://deb.debian.org/debian trixie/main armhf libsasl2-modules-db armhf 2.1.28+dfsg1-8+b1 [18.6 kB] Get: 74 http://deb.debian.org/debian trixie/main armhf libsasl2-2 armhf 2.1.28+dfsg1-8+b1 [50.6 kB] Get: 75 http://deb.debian.org/debian trixie/main armhf libldap2 armhf 2.6.9+dfsg-1 [167 kB] Get: 76 http://deb.debian.org/debian trixie/main armhf libnghttp2-14 armhf 1.64.0-1 [62.9 kB] Get: 77 http://deb.debian.org/debian trixie/main armhf libnghttp3-9 armhf 1.6.0-2 [56.8 kB] Get: 78 http://deb.debian.org/debian trixie/main armhf libngtcp2-16 armhf 1.9.1-1 [115 kB] Get: 79 http://deb.debian.org/debian trixie/main armhf libngtcp2-crypto-gnutls8 armhf 1.9.1-1 [15.8 kB] Get: 80 http://deb.debian.org/debian trixie/main armhf libpsl5t64 armhf 0.21.2-1.1+b1 [55.8 kB] Get: 81 http://deb.debian.org/debian trixie/main armhf librtmp1 armhf 2.4+20151223.gitfa8646d.1-2+b5 [53.2 kB] Get: 82 http://deb.debian.org/debian trixie/main armhf libssh2-1t64 armhf 1.11.1-1 [227 kB] Get: 83 http://deb.debian.org/debian trixie/main armhf libcurl3t64-gnutls armhf 8.11.1-1+b1 [311 kB] Get: 84 http://deb.debian.org/debian trixie/main armhf liberror-perl all 0.17029-2 [29.0 kB] Get: 85 http://deb.debian.org/debian trixie/main armhf git-man all 1:2.47.2-0.1 [2205 kB] Get: 86 http://deb.debian.org/debian trixie/main armhf git armhf 1:2.47.2-0.1 [6556 kB] Get: 87 http://deb.debian.org/debian trixie/main armhf libtirpc-common all 1.3.4+ds-1.3 [10.9 kB] Get: 88 http://deb.debian.org/debian trixie/main armhf libtirpc3t64 armhf 1.3.4+ds-1.3+b1 [71.3 kB] Get: 89 http://deb.debian.org/debian trixie/main armhf libnsl2 armhf 1.3.0-3+b3 [35.0 kB] Get: 90 http://deb.debian.org/debian trixie/main armhf libpython3.12-stdlib armhf 3.12.8-5+b1 [1831 kB] Get: 91 http://deb.debian.org/debian trixie/main armhf localehelper all 0.1.4-3.1 [7928 B] Get: 92 http://deb.debian.org/debian trixie/main armhf python3.12 armhf 3.12.8-5+b1 [678 kB] Get: 93 http://deb.debian.org/debian trixie/main armhf python3-all armhf 3.13.1-2 [1056 B] Get: 94 http://deb.debian.org/debian trixie/main armhf python3-ansi all 0.1.5-2 [6232 B] Get: 95 http://deb.debian.org/debian trixie/main armhf python3-bcrypt armhf 4.2.0-2.1 [214 kB] Get: 96 http://deb.debian.org/debian trixie/main armhf python3-blinker all 1.9.0-1 [12.6 kB] Get: 97 http://deb.debian.org/debian trixie/main armhf python3-bottle all 0.13.2-1.1 [54.3 kB] Get: 98 http://deb.debian.org/debian trixie/main armhf python3-soupsieve all 2.6-1 [38.3 kB] Get: 99 http://deb.debian.org/debian trixie/main armhf python3-bs4 all 4.12.3-3 [133 kB] Get: 100 http://deb.debian.org/debian trixie/main armhf python3-certifi all 2024.12.14+ds-1 [9624 B] Get: 101 http://deb.debian.org/debian trixie/main armhf python3-cffi-backend armhf 1.17.1-2+b1 [83.7 kB] Get: 102 http://deb.debian.org/debian trixie/main armhf python3-chardet all 5.2.0+dfsg-2 [108 kB] Get: 103 http://deb.debian.org/debian trixie/main armhf python3-charset-normalizer armhf 3.4.0-1+b1 [126 kB] Get: 104 http://deb.debian.org/debian trixie/main armhf python3-click all 8.1.8-1 [95.2 kB] Get: 105 http://deb.debian.org/debian trixie/main armhf python3-colorlog all 6.9.0-1 [27.5 kB] Get: 106 http://deb.debian.org/debian trixie/main armhf python3-cryptography armhf 43.0.0-1 [827 kB] Get: 107 http://deb.debian.org/debian trixie/main armhf python3-daemonize all 2.5.0-3 [6324 B] Get: 108 http://deb.debian.org/debian trixie/main armhf python3-urllib3 all 2.3.0-1 [114 kB] Get: 109 http://deb.debian.org/debian trixie/main armhf python3-dulwich armhf 0.22.7-1 [205 kB] Get: 110 http://deb.debian.org/debian trixie/main armhf python3-itsdangerous all 2.2.0-2 [18.1 kB] Get: 111 http://deb.debian.org/debian trixie/main armhf python3-markupsafe armhf 2.1.5-1+b2 [13.4 kB] Get: 112 http://deb.debian.org/debian trixie/main armhf python3-jinja2 all 3.1.5-1 [107 kB] Get: 113 http://deb.debian.org/debian trixie/main armhf python3-werkzeug all 3.1.3-2 [207 kB] Get: 114 http://deb.debian.org/debian trixie/main armhf python3-flask all 3.1.0-2 [106 kB] Get: 115 http://deb.debian.org/debian trixie/main armhf python3-idna all 3.8-2 [41.6 kB] Get: 116 http://deb.debian.org/debian trixie/main armhf python3-iniconfig all 1.1.1-2 [6396 B] Get: 117 http://deb.debian.org/debian trixie/main armhf python3-legacy-cgi all 2.6.1-2 [16.1 kB] Get: 118 http://deb.debian.org/debian trixie/main armhf python3-markdown all 3.7-2 [85.2 kB] Get: 119 http://deb.debian.org/debian trixie/main armhf python3-openssl all 25.0.0-1 [52.1 kB] Get: 120 http://deb.debian.org/debian trixie/main armhf python3-packaging all 24.2-1 [55.3 kB] Get: 121 http://deb.debian.org/debian trixie/main armhf python3-tempita all 0.6.0-1 [14.6 kB] Get: 122 http://deb.debian.org/debian trixie/main armhf python3-paste all 3.10.1-1 [222 kB] Get: 123 http://deb.debian.org/debian trixie/main armhf python3-pastedeploy-tpl all 3.1-1 [8268 B] Get: 124 http://deb.debian.org/debian trixie/main armhf python3-pastedeploy all 3.1-1 [18.3 kB] Get: 125 http://deb.debian.org/debian trixie/main armhf python3-pluggy all 1.5.0-1 [26.9 kB] Get: 126 http://deb.debian.org/debian trixie/main armhf python3-pygments all 2.18.0+dfsg-2 [836 kB] Get: 127 http://deb.debian.org/debian trixie/main armhf python3-pytest all 8.3.4-1 [250 kB] Get: 128 http://deb.debian.org/debian trixie/main armhf python3-requests all 2.32.3+dfsg-1 [71.9 kB] Get: 129 http://deb.debian.org/debian trixie/main armhf python3-waitress all 3.0.2-1 [46.5 kB] Get: 130 http://deb.debian.org/debian trixie/main armhf python3-webob all 1:1.8.9-1 [89.1 kB] Get: 131 http://deb.debian.org/debian trixie/main armhf python3-webtest all 3.0.2-1 [35.2 kB] Get: 132 http://deb.debian.org/debian trixie/main armhf python3-yapsy all 1.12.2-3 [30.4 kB] Fetched 54.8 MB in 7s (7936 kB/s) Preconfiguring packages ... Selecting previously unselected package libpython3.13-minimal:armhf. (Reading database ... (Reading database ... 5% (Reading database ... 10% (Reading database ... 15% (Reading database ... 20% (Reading database ... 25% (Reading database ... 30% (Reading database ... 35% (Reading database ... 40% (Reading database ... 45% (Reading database ... 50% (Reading database ... 55% (Reading database ... 60% (Reading database ... 65% (Reading database ... 70% (Reading database ... 75% (Reading database ... 80% (Reading database ... 85% (Reading database ... 90% (Reading database ... 95% (Reading database ... 100% (Reading database ... 19573 files and directories currently installed.) Preparing to unpack .../libpython3.13-minimal_3.13.1-3+b1_armhf.deb ... Unpacking libpython3.13-minimal:armhf (3.13.1-3+b1) ... Selecting previously unselected package libexpat1:armhf. Preparing to unpack .../libexpat1_2.6.4-1_armhf.deb ... Unpacking libexpat1:armhf (2.6.4-1) ... Selecting previously unselected package python3.13-minimal. Preparing to unpack .../python3.13-minimal_3.13.1-3+b1_armhf.deb ... Unpacking python3.13-minimal (3.13.1-3+b1) ... Setting up libpython3.13-minimal:armhf (3.13.1-3+b1) ... Setting up libexpat1:armhf (2.6.4-1) ... Setting up python3.13-minimal (3.13.1-3+b1) ... Selecting previously unselected package python3-minimal. (Reading database ... (Reading database ... 5% (Reading database ... 10% (Reading database ... 15% (Reading database ... 20% (Reading database ... 25% (Reading database ... 30% (Reading database ... 35% (Reading database ... 40% (Reading database ... 45% (Reading database ... 50% (Reading database ... 55% (Reading database ... 60% (Reading database ... 65% (Reading database ... 70% (Reading database ... 75% (Reading database ... 80% (Reading database ... 85% (Reading database ... 90% (Reading database ... 95% (Reading database ... 100% (Reading database ... 19909 files and directories currently installed.) Preparing to unpack .../0-python3-minimal_3.13.1-2_armhf.deb ... Unpacking python3-minimal (3.13.1-2) ... Selecting previously unselected package media-types. Preparing to unpack .../1-media-types_10.1.0_all.deb ... Unpacking media-types (10.1.0) ... Selecting previously unselected package netbase. Preparing to unpack .../2-netbase_6.4_all.deb ... Unpacking netbase (6.4) ... Selecting previously unselected package tzdata. Preparing to unpack .../3-tzdata_2024b-6_all.deb ... Unpacking tzdata (2024b-6) ... Selecting previously unselected package libffi8:armhf. Preparing to unpack .../4-libffi8_3.4.6-1_armhf.deb ... Unpacking libffi8:armhf (3.4.6-1) ... Selecting previously unselected package readline-common. Preparing to unpack .../5-readline-common_8.2-6_all.deb ... Unpacking readline-common (8.2-6) ... Selecting previously unselected package libreadline8t64:armhf. Preparing to unpack .../6-libreadline8t64_8.2-6_armhf.deb ... Adding 'diversion of /lib/arm-linux-gnueabihf/libhistory.so.8 to /lib/arm-linux-gnueabihf/libhistory.so.8.usr-is-merged by libreadline8t64' Adding 'diversion of /lib/arm-linux-gnueabihf/libhistory.so.8.2 to /lib/arm-linux-gnueabihf/libhistory.so.8.2.usr-is-merged by libreadline8t64' Adding 'diversion of /lib/arm-linux-gnueabihf/libreadline.so.8 to /lib/arm-linux-gnueabihf/libreadline.so.8.usr-is-merged by libreadline8t64' Adding 'diversion of /lib/arm-linux-gnueabihf/libreadline.so.8.2 to /lib/arm-linux-gnueabihf/libreadline.so.8.2.usr-is-merged by libreadline8t64' Unpacking libreadline8t64:armhf (8.2-6) ... Selecting previously unselected package libpython3.13-stdlib:armhf. Preparing to unpack .../7-libpython3.13-stdlib_3.13.1-3+b1_armhf.deb ... Unpacking libpython3.13-stdlib:armhf (3.13.1-3+b1) ... Selecting previously unselected package python3.13. Preparing to unpack .../8-python3.13_3.13.1-3+b1_armhf.deb ... Unpacking python3.13 (3.13.1-3+b1) ... Selecting previously unselected package libpython3-stdlib:armhf. Preparing to unpack .../9-libpython3-stdlib_3.13.1-2_armhf.deb ... Unpacking libpython3-stdlib:armhf (3.13.1-2) ... Setting up python3-minimal (3.13.1-2) ... Selecting previously unselected package python3. (Reading database ... (Reading database ... 5% (Reading database ... 10% (Reading database ... 15% (Reading database ... 20% (Reading database ... 25% (Reading database ... 30% (Reading database ... 35% (Reading database ... 40% (Reading database ... 45% (Reading database ... 50% (Reading database ... 55% (Reading database ... 60% (Reading database ... 65% (Reading database ... 70% (Reading database ... 75% (Reading database ... 80% (Reading database ... 85% (Reading database ... 90% (Reading database ... 95% (Reading database ... 100% (Reading database ... 20920 files and directories currently installed.) Preparing to unpack .../000-python3_3.13.1-2_armhf.deb ... Unpacking python3 (3.13.1-2) ... Selecting previously unselected package libpython3.12-minimal:armhf. Preparing to unpack .../001-libpython3.12-minimal_3.12.8-5+b1_armhf.deb ... Unpacking libpython3.12-minimal:armhf (3.12.8-5+b1) ... Selecting previously unselected package python3.12-minimal. Preparing to unpack .../002-python3.12-minimal_3.12.8-5+b1_armhf.deb ... Unpacking python3.12-minimal (3.12.8-5+b1) ... Selecting previously unselected package sensible-utils. Preparing to unpack .../003-sensible-utils_0.0.24_all.deb ... Unpacking sensible-utils (0.0.24) ... Selecting previously unselected package openssl. Preparing to unpack .../004-openssl_3.4.0-2_armhf.deb ... Unpacking openssl (3.4.0-2) ... Selecting previously unselected package ca-certificates. Preparing to unpack .../005-ca-certificates_20241223_all.deb ... Unpacking ca-certificates (20241223) ... Selecting previously unselected package libmagic-mgc. Preparing to unpack .../006-libmagic-mgc_1%3a5.45-3+b1_armhf.deb ... Unpacking libmagic-mgc (1:5.45-3+b1) ... Selecting previously unselected package libmagic1t64:armhf. Preparing to unpack .../007-libmagic1t64_1%3a5.45-3+b1_armhf.deb ... Unpacking libmagic1t64:armhf (1:5.45-3+b1) ... Selecting previously unselected package file. Preparing to unpack .../008-file_1%3a5.45-3+b1_armhf.deb ... Unpacking file (1:5.45-3+b1) ... Selecting previously unselected package gettext-base. Preparing to unpack .../009-gettext-base_0.23.1-1_armhf.deb ... Unpacking gettext-base (0.23.1-1) ... Selecting previously unselected package libuchardet0:armhf. Preparing to unpack .../010-libuchardet0_0.0.8-1+b2_armhf.deb ... Unpacking libuchardet0:armhf (0.0.8-1+b2) ... Selecting previously unselected package groff-base. Preparing to unpack .../011-groff-base_1.23.0-7_armhf.deb ... Unpacking groff-base (1.23.0-7) ... Selecting previously unselected package locales. Preparing to unpack .../012-locales_2.40-6_all.deb ... Unpacking locales (2.40-6) ... Selecting previously unselected package bsdextrautils. Preparing to unpack .../013-bsdextrautils_2.40.4-2_armhf.deb ... Unpacking bsdextrautils (2.40.4-2) ... Selecting previously unselected package libpipeline1:armhf. Preparing to unpack .../014-libpipeline1_1.5.8-1_armhf.deb ... Unpacking libpipeline1:armhf (1.5.8-1) ... Selecting previously unselected package man-db. Preparing to unpack .../015-man-db_2.13.0-1_armhf.deb ... Unpacking man-db (2.13.0-1) ... Selecting previously unselected package m4. Preparing to unpack .../016-m4_1.4.19-5_armhf.deb ... Unpacking m4 (1.4.19-5) ... Selecting previously unselected package autoconf. Preparing to unpack .../017-autoconf_2.72-3_all.deb ... Unpacking autoconf (2.72-3) ... Selecting previously unselected package autotools-dev. Preparing to unpack .../018-autotools-dev_20220109.1_all.deb ... Unpacking autotools-dev (20220109.1) ... Selecting previously unselected package automake. Preparing to unpack .../019-automake_1%3a1.16.5-1.3_all.deb ... Unpacking automake (1:1.16.5-1.3) ... Selecting previously unselected package autopoint. Preparing to unpack .../020-autopoint_0.23.1-1_all.deb ... Unpacking autopoint (0.23.1-1) ... Selecting previously unselected package libdebhelper-perl. Preparing to unpack .../021-libdebhelper-perl_13.24.1_all.deb ... Unpacking libdebhelper-perl (13.24.1) ... Selecting previously unselected package libtool. Preparing to unpack .../022-libtool_2.5.4-2_all.deb ... Unpacking libtool (2.5.4-2) ... Selecting previously unselected package dh-autoreconf. Preparing to unpack .../023-dh-autoreconf_20_all.deb ... Unpacking dh-autoreconf (20) ... Selecting previously unselected package libarchive-zip-perl. Preparing to unpack .../024-libarchive-zip-perl_1.68-1_all.deb ... Unpacking libarchive-zip-perl (1.68-1) ... Selecting previously unselected package libfile-stripnondeterminism-perl. Preparing to unpack .../025-libfile-stripnondeterminism-perl_1.14.1-1_all.deb ... Unpacking libfile-stripnondeterminism-perl (1.14.1-1) ... Selecting previously unselected package dh-strip-nondeterminism. Preparing to unpack .../026-dh-strip-nondeterminism_1.14.1-1_all.deb ... Unpacking dh-strip-nondeterminism (1.14.1-1) ... Selecting previously unselected package libelf1t64:armhf. Preparing to unpack .../027-libelf1t64_0.192-4_armhf.deb ... Unpacking libelf1t64:armhf (0.192-4) ... Selecting previously unselected package dwz. Preparing to unpack .../028-dwz_0.15-1+b2_armhf.deb ... Unpacking dwz (0.15-1+b2) ... Selecting previously unselected package libunistring5:armhf. Preparing to unpack .../029-libunistring5_1.3-1_armhf.deb ... Unpacking libunistring5:armhf (1.3-1) ... Selecting previously unselected package libicu72:armhf. Preparing to unpack .../030-libicu72_72.1-6_armhf.deb ... Unpacking libicu72:armhf (72.1-6) ... Selecting previously unselected package libxml2:armhf. Preparing to unpack .../031-libxml2_2.12.7+dfsg+really2.9.14-0.2+b1_armhf.deb ... Unpacking libxml2:armhf (2.12.7+dfsg+really2.9.14-0.2+b1) ... Selecting previously unselected package gettext. Preparing to unpack .../032-gettext_0.23.1-1_armhf.deb ... Unpacking gettext (0.23.1-1) ... Selecting previously unselected package intltool-debian. Preparing to unpack .../033-intltool-debian_0.35.0+20060710.6_all.deb ... Unpacking intltool-debian (0.35.0+20060710.6) ... Selecting previously unselected package po-debconf. Preparing to unpack .../034-po-debconf_1.0.21+nmu1_all.deb ... Unpacking po-debconf (1.0.21+nmu1) ... Selecting previously unselected package debhelper. Preparing to unpack .../035-debhelper_13.24.1_all.deb ... Unpacking debhelper (13.24.1) ... Selecting previously unselected package python3-autocommand. Preparing to unpack .../036-python3-autocommand_2.2.2-3_all.deb ... Unpacking python3-autocommand (2.2.2-3) ... Selecting previously unselected package python3-more-itertools. Preparing to unpack .../037-python3-more-itertools_10.6.0-1_all.deb ... Unpacking python3-more-itertools (10.6.0-1) ... Selecting previously unselected package python3-typing-extensions. Preparing to unpack .../038-python3-typing-extensions_4.12.2-2_all.deb ... Unpacking python3-typing-extensions (4.12.2-2) ... Selecting previously unselected package python3-typeguard. Preparing to unpack .../039-python3-typeguard_4.4.1-1_all.deb ... Unpacking python3-typeguard (4.4.1-1) ... Selecting previously unselected package python3-inflect. Preparing to unpack .../040-python3-inflect_7.3.1-2_all.deb ... Unpacking python3-inflect (7.3.1-2) ... Selecting previously unselected package python3-jaraco.context. Preparing to unpack .../041-python3-jaraco.context_6.0.0-1_all.deb ... Unpacking python3-jaraco.context (6.0.0-1) ... Selecting previously unselected package python3-jaraco.functools. Preparing to unpack .../042-python3-jaraco.functools_4.1.0-1_all.deb ... Unpacking python3-jaraco.functools (4.1.0-1) ... Selecting previously unselected package python3-pkg-resources. Preparing to unpack .../043-python3-pkg-resources_75.6.0-1_all.deb ... Unpacking python3-pkg-resources (75.6.0-1) ... Selecting previously unselected package python3-jaraco.text. Preparing to unpack .../044-python3-jaraco.text_4.0.0-1_all.deb ... Unpacking python3-jaraco.text (4.0.0-1) ... Selecting previously unselected package python3-zipp. Preparing to unpack .../045-python3-zipp_3.21.0-1_all.deb ... Unpacking python3-zipp (3.21.0-1) ... Selecting previously unselected package python3-setuptools. Preparing to unpack .../046-python3-setuptools_75.6.0-1_all.deb ... Unpacking python3-setuptools (75.6.0-1) ... Selecting previously unselected package dh-python. Preparing to unpack .../047-dh-python_6.20250108_all.deb ... Unpacking dh-python (6.20250108) ... Selecting previously unselected package libbrotli1:armhf. Preparing to unpack .../048-libbrotli1_1.1.0-2+b6_armhf.deb ... Unpacking libbrotli1:armhf (1.1.0-2+b6) ... Selecting previously unselected package libidn2-0:armhf. Preparing to unpack .../049-libidn2-0_2.3.7-2+b1_armhf.deb ... Unpacking libidn2-0:armhf (2.3.7-2+b1) ... Selecting previously unselected package libp11-kit0:armhf. Preparing to unpack .../050-libp11-kit0_0.25.5-3_armhf.deb ... Unpacking libp11-kit0:armhf (0.25.5-3) ... Selecting previously unselected package libtasn1-6:armhf. Preparing to unpack .../051-libtasn1-6_4.19.0-3+b3_armhf.deb ... Unpacking libtasn1-6:armhf (4.19.0-3+b3) ... Selecting previously unselected package libgnutls30t64:armhf. Preparing to unpack .../052-libgnutls30t64_3.8.8-2_armhf.deb ... Unpacking libgnutls30t64:armhf (3.8.8-2) ... Selecting previously unselected package libkrb5support0:armhf. Preparing to unpack .../053-libkrb5support0_1.21.3-4_armhf.deb ... Unpacking libkrb5support0:armhf (1.21.3-4) ... Selecting previously unselected package libcom-err2:armhf. Preparing to unpack .../054-libcom-err2_1.47.2-1_armhf.deb ... Unpacking libcom-err2:armhf (1.47.2-1) ... Selecting previously unselected package libk5crypto3:armhf. Preparing to unpack .../055-libk5crypto3_1.21.3-4_armhf.deb ... Unpacking libk5crypto3:armhf (1.21.3-4) ... Selecting previously unselected package libkeyutils1:armhf. Preparing to unpack .../056-libkeyutils1_1.6.3-4_armhf.deb ... Unpacking libkeyutils1:armhf (1.6.3-4) ... Selecting previously unselected package libkrb5-3:armhf. Preparing to unpack .../057-libkrb5-3_1.21.3-4_armhf.deb ... Unpacking libkrb5-3:armhf (1.21.3-4) ... Selecting previously unselected package libgssapi-krb5-2:armhf. Preparing to unpack .../058-libgssapi-krb5-2_1.21.3-4_armhf.deb ... Unpacking libgssapi-krb5-2:armhf (1.21.3-4) ... Selecting previously unselected package libsasl2-modules-db:armhf. Preparing to unpack .../059-libsasl2-modules-db_2.1.28+dfsg1-8+b1_armhf.deb ... Unpacking libsasl2-modules-db:armhf (2.1.28+dfsg1-8+b1) ... Selecting previously unselected package libsasl2-2:armhf. Preparing to unpack .../060-libsasl2-2_2.1.28+dfsg1-8+b1_armhf.deb ... Unpacking libsasl2-2:armhf (2.1.28+dfsg1-8+b1) ... Selecting previously unselected package libldap2:armhf. Preparing to unpack .../061-libldap2_2.6.9+dfsg-1_armhf.deb ... Unpacking libldap2:armhf (2.6.9+dfsg-1) ... Selecting previously unselected package libnghttp2-14:armhf. Preparing to unpack .../062-libnghttp2-14_1.64.0-1_armhf.deb ... Unpacking libnghttp2-14:armhf (1.64.0-1) ... Selecting previously unselected package libnghttp3-9:armhf. Preparing to unpack .../063-libnghttp3-9_1.6.0-2_armhf.deb ... Unpacking libnghttp3-9:armhf (1.6.0-2) ... Selecting previously unselected package libngtcp2-16:armhf. Preparing to unpack .../064-libngtcp2-16_1.9.1-1_armhf.deb ... Unpacking libngtcp2-16:armhf (1.9.1-1) ... Selecting previously unselected package libngtcp2-crypto-gnutls8:armhf. Preparing to unpack .../065-libngtcp2-crypto-gnutls8_1.9.1-1_armhf.deb ... Unpacking libngtcp2-crypto-gnutls8:armhf (1.9.1-1) ... Selecting previously unselected package libpsl5t64:armhf. Preparing to unpack .../066-libpsl5t64_0.21.2-1.1+b1_armhf.deb ... Unpacking libpsl5t64:armhf (0.21.2-1.1+b1) ... Selecting previously unselected package librtmp1:armhf. Preparing to unpack .../067-librtmp1_2.4+20151223.gitfa8646d.1-2+b5_armhf.deb ... Unpacking librtmp1:armhf (2.4+20151223.gitfa8646d.1-2+b5) ... Selecting previously unselected package libssh2-1t64:armhf. Preparing to unpack .../068-libssh2-1t64_1.11.1-1_armhf.deb ... Unpacking libssh2-1t64:armhf (1.11.1-1) ... Selecting previously unselected package libcurl3t64-gnutls:armhf. Preparing to unpack .../069-libcurl3t64-gnutls_8.11.1-1+b1_armhf.deb ... Unpacking libcurl3t64-gnutls:armhf (8.11.1-1+b1) ... Selecting previously unselected package liberror-perl. Preparing to unpack .../070-liberror-perl_0.17029-2_all.deb ... Unpacking liberror-perl (0.17029-2) ... Selecting previously unselected package git-man. Preparing to unpack .../071-git-man_1%3a2.47.2-0.1_all.deb ... Unpacking git-man (1:2.47.2-0.1) ... Selecting previously unselected package git. Preparing to unpack .../072-git_1%3a2.47.2-0.1_armhf.deb ... Unpacking git (1:2.47.2-0.1) ... Selecting previously unselected package libtirpc-common. Preparing to unpack .../073-libtirpc-common_1.3.4+ds-1.3_all.deb ... Unpacking libtirpc-common (1.3.4+ds-1.3) ... Selecting previously unselected package libtirpc3t64:armhf. Preparing to unpack .../074-libtirpc3t64_1.3.4+ds-1.3+b1_armhf.deb ... Adding 'diversion of /lib/arm-linux-gnueabihf/libtirpc.so.3 to /lib/arm-linux-gnueabihf/libtirpc.so.3.usr-is-merged by libtirpc3t64' Adding 'diversion of /lib/arm-linux-gnueabihf/libtirpc.so.3.0.0 to /lib/arm-linux-gnueabihf/libtirpc.so.3.0.0.usr-is-merged by libtirpc3t64' Unpacking libtirpc3t64:armhf (1.3.4+ds-1.3+b1) ... Selecting previously unselected package libnsl2:armhf. Preparing to unpack .../075-libnsl2_1.3.0-3+b3_armhf.deb ... Unpacking libnsl2:armhf (1.3.0-3+b3) ... Selecting previously unselected package libpython3.12-stdlib:armhf. Preparing to unpack .../076-libpython3.12-stdlib_3.12.8-5+b1_armhf.deb ... Unpacking libpython3.12-stdlib:armhf (3.12.8-5+b1) ... Selecting previously unselected package localehelper. Preparing to unpack .../077-localehelper_0.1.4-3.1_all.deb ... Unpacking localehelper (0.1.4-3.1) ... Selecting previously unselected package python3.12. Preparing to unpack .../078-python3.12_3.12.8-5+b1_armhf.deb ... Unpacking python3.12 (3.12.8-5+b1) ... Selecting previously unselected package python3-all. Preparing to unpack .../079-python3-all_3.13.1-2_armhf.deb ... Unpacking python3-all (3.13.1-2) ... Selecting previously unselected package python3-ansi. Preparing to unpack .../080-python3-ansi_0.1.5-2_all.deb ... Unpacking python3-ansi (0.1.5-2) ... Selecting previously unselected package python3-bcrypt. Preparing to unpack .../081-python3-bcrypt_4.2.0-2.1_armhf.deb ... Unpacking python3-bcrypt (4.2.0-2.1) ... Selecting previously unselected package python3-blinker. Preparing to unpack .../082-python3-blinker_1.9.0-1_all.deb ... Unpacking python3-blinker (1.9.0-1) ... Selecting previously unselected package python3-bottle. Preparing to unpack .../083-python3-bottle_0.13.2-1.1_all.deb ... Unpacking python3-bottle (0.13.2-1.1) ... Selecting previously unselected package python3-soupsieve. Preparing to unpack .../084-python3-soupsieve_2.6-1_all.deb ... Unpacking python3-soupsieve (2.6-1) ... Selecting previously unselected package python3-bs4. Preparing to unpack .../085-python3-bs4_4.12.3-3_all.deb ... Unpacking python3-bs4 (4.12.3-3) ... Selecting previously unselected package python3-certifi. Preparing to unpack .../086-python3-certifi_2024.12.14+ds-1_all.deb ... Unpacking python3-certifi (2024.12.14+ds-1) ... Selecting previously unselected package python3-cffi-backend:armhf. Preparing to unpack .../087-python3-cffi-backend_1.17.1-2+b1_armhf.deb ... Unpacking python3-cffi-backend:armhf (1.17.1-2+b1) ... Selecting previously unselected package python3-chardet. Preparing to unpack .../088-python3-chardet_5.2.0+dfsg-2_all.deb ... Unpacking python3-chardet (5.2.0+dfsg-2) ... Selecting previously unselected package python3-charset-normalizer. Preparing to unpack .../089-python3-charset-normalizer_3.4.0-1+b1_armhf.deb ... Unpacking python3-charset-normalizer (3.4.0-1+b1) ... Selecting previously unselected package python3-click. Preparing to unpack .../090-python3-click_8.1.8-1_all.deb ... Unpacking python3-click (8.1.8-1) ... Selecting previously unselected package python3-colorlog. Preparing to unpack .../091-python3-colorlog_6.9.0-1_all.deb ... Unpacking python3-colorlog (6.9.0-1) ... Selecting previously unselected package python3-cryptography. Preparing to unpack .../092-python3-cryptography_43.0.0-1_armhf.deb ... Unpacking python3-cryptography (43.0.0-1) ... Selecting previously unselected package python3-daemonize. Preparing to unpack .../093-python3-daemonize_2.5.0-3_all.deb ... Unpacking python3-daemonize (2.5.0-3) ... Selecting previously unselected package python3-urllib3. Preparing to unpack .../094-python3-urllib3_2.3.0-1_all.deb ... Unpacking python3-urllib3 (2.3.0-1) ... Selecting previously unselected package python3-dulwich. Preparing to unpack .../095-python3-dulwich_0.22.7-1_armhf.deb ... Unpacking python3-dulwich (0.22.7-1) ... Selecting previously unselected package python3-itsdangerous. Preparing to unpack .../096-python3-itsdangerous_2.2.0-2_all.deb ... Unpacking python3-itsdangerous (2.2.0-2) ... Selecting previously unselected package python3-markupsafe. Preparing to unpack .../097-python3-markupsafe_2.1.5-1+b2_armhf.deb ... Unpacking python3-markupsafe (2.1.5-1+b2) ... Selecting previously unselected package python3-jinja2. Preparing to unpack .../098-python3-jinja2_3.1.5-1_all.deb ... Unpacking python3-jinja2 (3.1.5-1) ... Selecting previously unselected package python3-werkzeug. Preparing to unpack .../099-python3-werkzeug_3.1.3-2_all.deb ... Unpacking python3-werkzeug (3.1.3-2) ... Selecting previously unselected package python3-flask. Preparing to unpack .../100-python3-flask_3.1.0-2_all.deb ... Unpacking python3-flask (3.1.0-2) ... Selecting previously unselected package python3-idna. Preparing to unpack .../101-python3-idna_3.8-2_all.deb ... Unpacking python3-idna (3.8-2) ... Selecting previously unselected package python3-iniconfig. Preparing to unpack .../102-python3-iniconfig_1.1.1-2_all.deb ... Unpacking python3-iniconfig (1.1.1-2) ... Selecting previously unselected package python3-legacy-cgi. Preparing to unpack .../103-python3-legacy-cgi_2.6.1-2_all.deb ... Unpacking python3-legacy-cgi (2.6.1-2) ... Selecting previously unselected package python3-markdown. Preparing to unpack .../104-python3-markdown_3.7-2_all.deb ... Unpacking python3-markdown (3.7-2) ... Selecting previously unselected package python3-openssl. Preparing to unpack .../105-python3-openssl_25.0.0-1_all.deb ... Unpacking python3-openssl (25.0.0-1) ... Selecting previously unselected package python3-packaging. Preparing to unpack .../106-python3-packaging_24.2-1_all.deb ... Unpacking python3-packaging (24.2-1) ... Selecting previously unselected package python3-tempita. Preparing to unpack .../107-python3-tempita_0.6.0-1_all.deb ... Unpacking python3-tempita (0.6.0-1) ... Selecting previously unselected package python3-paste. Preparing to unpack .../108-python3-paste_3.10.1-1_all.deb ... Unpacking python3-paste (3.10.1-1) ... Selecting previously unselected package python3-pastedeploy-tpl. Preparing to unpack .../109-python3-pastedeploy-tpl_3.1-1_all.deb ... Unpacking python3-pastedeploy-tpl (3.1-1) ... Selecting previously unselected package python3-pastedeploy. Preparing to unpack .../110-python3-pastedeploy_3.1-1_all.deb ... Unpacking python3-pastedeploy (3.1-1) ... Selecting previously unselected package python3-pluggy. Preparing to unpack .../111-python3-pluggy_1.5.0-1_all.deb ... Unpacking python3-pluggy (1.5.0-1) ... Selecting previously unselected package python3-pygments. Preparing to unpack .../112-python3-pygments_2.18.0+dfsg-2_all.deb ... Unpacking python3-pygments (2.18.0+dfsg-2) ... Selecting previously unselected package python3-pytest. Preparing to unpack .../113-python3-pytest_8.3.4-1_all.deb ... Unpacking python3-pytest (8.3.4-1) ... Selecting previously unselected package python3-requests. Preparing to unpack .../114-python3-requests_2.32.3+dfsg-1_all.deb ... Unpacking python3-requests (2.32.3+dfsg-1) ... Selecting previously unselected package python3-waitress. Preparing to unpack .../115-python3-waitress_3.0.2-1_all.deb ... Unpacking python3-waitress (3.0.2-1) ... Selecting previously unselected package python3-webob. Preparing to unpack .../116-python3-webob_1%3a1.8.9-1_all.deb ... Unpacking python3-webob (1:1.8.9-1) ... Selecting previously unselected package python3-webtest. Preparing to unpack .../117-python3-webtest_3.0.2-1_all.deb ... Unpacking python3-webtest (3.0.2-1) ... Selecting previously unselected package python3-yapsy. Preparing to unpack .../118-python3-yapsy_1.12.2-3_all.deb ... Unpacking python3-yapsy (1.12.2-3) ... Setting up media-types (10.1.0) ... Setting up libpipeline1:armhf (1.5.8-1) ... Setting up libkeyutils1:armhf (1.6.3-4) ... Setting up libicu72:armhf (72.1-6) ... Setting up bsdextrautils (2.40.4-2) ... Setting up libmagic-mgc (1:5.45-3+b1) ... Setting up libarchive-zip-perl (1.68-1) ... Setting up libtirpc-common (1.3.4+ds-1.3) ... Setting up libdebhelper-perl (13.24.1) ... Setting up libbrotli1:armhf (1.1.0-2+b6) ... Setting up libmagic1t64:armhf (1:5.45-3+b1) ... Setting up libpython3.12-minimal:armhf (3.12.8-5+b1) ... Setting up libnghttp2-14:armhf (1.64.0-1) ... Setting up gettext-base (0.23.1-1) ... Setting up m4 (1.4.19-5) ... Setting up libcom-err2:armhf (1.47.2-1) ... Setting up file (1:5.45-3+b1) ... Setting up locales (2.40-6) ... locales-all installed, skipping locales generation Setting up libelf1t64:armhf (0.192-4) ... Setting up libkrb5support0:armhf (1.21.3-4) ... Setting up libsasl2-modules-db:armhf (2.1.28+dfsg1-8+b1) ... Setting up tzdata (2024b-6) ... Current default time zone: 'Etc/UTC' Local time is now: Sun Feb 2 15:36:04 UTC 2025. Universal Time is now: Sun Feb 2 15:36:04 UTC 2025. Run 'dpkg-reconfigure tzdata' if you wish to change it. Setting up liberror-perl (0.17029-2) ... Setting up autotools-dev (20220109.1) ... Setting up localehelper (0.1.4-3.1) ... Setting up python3-pastedeploy-tpl (3.1-1) ... Setting up libunistring5:armhf (1.3-1) ... Setting up autopoint (0.23.1-1) ... Setting up libk5crypto3:armhf (1.21.3-4) ... Setting up libsasl2-2:armhf (2.1.28+dfsg1-8+b1) ... Setting up autoconf (2.72-3) ... Setting up libnghttp3-9:armhf (1.6.0-2) ... Setting up libffi8:armhf (3.4.6-1) ... Setting up dwz (0.15-1+b2) ... Setting up sensible-utils (0.0.24) ... Setting up libuchardet0:armhf (0.0.8-1+b2) ... Setting up libtasn1-6:armhf (4.19.0-3+b3) ... Setting up git-man (1:2.47.2-0.1) ... Setting up netbase (6.4) ... Setting up libngtcp2-16:armhf (1.9.1-1) ... Setting up libkrb5-3:armhf (1.21.3-4) ... Setting up libssh2-1t64:armhf (1.11.1-1) ... Setting up openssl (3.4.0-2) ... Setting up readline-common (8.2-6) ... Setting up libxml2:armhf (2.12.7+dfsg+really2.9.14-0.2+b1) ... Setting up libldap2:armhf (2.6.9+dfsg-1) ... Setting up automake (1:1.16.5-1.3) ... update-alternatives: using /usr/bin/automake-1.16 to provide /usr/bin/automake (automake) in auto mode Setting up libfile-stripnondeterminism-perl (1.14.1-1) ... Setting up python3.12-minimal (3.12.8-5+b1) ... Setting up gettext (0.23.1-1) ... Setting up libtool (2.5.4-2) ... Setting up libidn2-0:armhf (2.3.7-2+b1) ... Setting up intltool-debian (0.35.0+20060710.6) ... Setting up dh-autoreconf (20) ... Setting up ca-certificates (20241223) ... Updating certificates in /etc/ssl/certs... 152 added, 0 removed; done. Setting up libp11-kit0:armhf (0.25.5-3) ... Setting up libgssapi-krb5-2:armhf (1.21.3-4) ... Setting up libreadline8t64:armhf (8.2-6) ... Setting up dh-strip-nondeterminism (1.14.1-1) ... Setting up groff-base (1.23.0-7) ... Setting up libpython3.13-stdlib:armhf (3.13.1-3+b1) ... Setting up libpython3-stdlib:armhf (3.13.1-2) ... Setting up libgnutls30t64:armhf (3.8.8-2) ... Setting up libtirpc3t64:armhf (1.3.4+ds-1.3+b1) ... Setting up python3.13 (3.13.1-3+b1) ... Setting up po-debconf (1.0.21+nmu1) ... Setting up libpsl5t64:armhf (0.21.2-1.1+b1) ... Setting up python3 (3.13.1-2) ... Setting up python3-zipp (3.21.0-1) ... Setting up python3-click (8.1.8-1) ... Setting up python3-autocommand (2.2.2-3) ... Setting up man-db (2.13.0-1) ... Not building database; man-db/auto-update is not 'true'. Setting up python3-markupsafe (2.1.5-1+b2) ... Setting up python3-jinja2 (3.1.5-1) ... Setting up python3-pygments (2.18.0+dfsg-2) ... Setting up python3-tempita (0.6.0-1) ... Setting up python3-packaging (24.2-1) ... Setting up python3-ansi (0.1.5-2) ... Setting up python3-chardet (5.2.0+dfsg-2) ... Setting up python3-certifi (2024.12.14+ds-1) ... Setting up librtmp1:armhf (2.4+20151223.gitfa8646d.1-2+b5) ... Setting up python3-werkzeug (3.1.3-2) ... Setting up python3-idna (3.8-2) ... Setting up python3-markdown (3.7-2) ... Setting up python3-typing-extensions (4.12.2-2) ... Setting up python3-urllib3 (2.3.0-1) ... Setting up python3-pluggy (1.5.0-1) ... Setting up python3-legacy-cgi (2.6.1-2) ... Setting up python3-dulwich (0.22.7-1) ... Setting up libnsl2:armhf (1.3.0-3+b3) ... Setting up python3-soupsieve (2.6-1) ... Setting up libngtcp2-crypto-gnutls8:armhf (1.9.1-1) ... Setting up python3-cffi-backend:armhf (1.17.1-2+b1) ... Setting up python3-webob (1:1.8.9-1) ... Setting up python3-yapsy (1.12.2-3) ... Setting up python3-blinker (1.9.0-1) ... Setting up python3-daemonize (2.5.0-3) ... Setting up python3-more-itertools (10.6.0-1) ... Setting up python3-iniconfig (1.1.1-2) ... Setting up python3-waitress (3.0.2-1) ... Setting up python3-jaraco.functools (4.1.0-1) ... Setting up libpython3.12-stdlib:armhf (3.12.8-5+b1) ... Setting up python3-bottle (0.13.2-1.1) ... Setting up python3-jaraco.context (6.0.0-1) ... Setting up python3-colorlog (6.9.0-1) ... Setting up python3-charset-normalizer (3.4.0-1+b1) ... Setting up python3-pytest (8.3.4-1) ... Setting up python3-bcrypt (4.2.0-2.1) ... Setting up python3.12 (3.12.8-5+b1) ... Setting up python3-typeguard (4.4.1-1) ... Setting up libcurl3t64-gnutls:armhf (8.11.1-1+b1) ... Setting up python3-itsdangerous (2.2.0-2) ... Setting up python3-all (3.13.1-2) ... Setting up debhelper (13.24.1) ... Setting up python3-bs4 (4.12.3-3) ... Setting up python3-inflect (7.3.1-2) ... Setting up python3-jaraco.text (4.0.0-1) ... Setting up python3-cryptography (43.0.0-1) ... Setting up python3-requests (2.32.3+dfsg-1) ... Setting up git (1:2.47.2-0.1) ... Setting up python3-pkg-resources (75.6.0-1) ... Setting up python3-setuptools (75.6.0-1) ... Setting up python3-openssl (25.0.0-1) ... Setting up python3-flask (3.1.0-2) ... Setting up python3-paste (3.10.1-1) ... Setting up dh-python (6.20250108) ... Setting up python3-pastedeploy (3.1-1) ... Setting up python3-webtest (3.0.2-1) ... Processing triggers for libc-bin (2.40-6) ... Processing triggers for ca-certificates (20241223) ... Updating certificates in /etc/ssl/certs... 0 added, 0 removed; done. Running hooks in /etc/ca-certificates/update.d... done. Reading package lists... Building dependency tree... Reading state information... Reading extended state information... Initializing package states... Writing extended state information... Building tag database... -> Finished parsing the build-deps I: Building the package I: Running cd /build/reproducible-path/errbot-6.2.0+ds/ && env PATH="/usr/sbin:/usr/bin:/sbin:/bin:/usr/games" HOME="/nonexistent/first-build" dpkg-buildpackage -us -uc -b && env PATH="/usr/sbin:/usr/bin:/sbin:/bin:/usr/games" HOME="/nonexistent/first-build" dpkg-genchanges -S > ../errbot_6.2.0+ds-4_source.changes dpkg-buildpackage: info: source package errbot dpkg-buildpackage: info: source version 6.2.0+ds-4 dpkg-buildpackage: info: source distribution unstable dpkg-buildpackage: info: source changed by Alexandre Detiste dpkg-source --before-build . dpkg-buildpackage: info: host architecture armhf debian/rules clean dh clean --buildsystem=pybuild dh_auto_clean -O--buildsystem=pybuild pybuild --clean -i python{version} -p "3.12 3.13" I: pybuild base:311: python3.12 setup.py clean /usr/lib/python3/dist-packages/setuptools/_distutils/dist.py:261: UserWarning: Unknown distribution option: 'tests_require' warnings.warn(msg) running clean removing '/build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build' (and everything under it) 'build/bdist.linux-armv7l' does not exist -- can't clean it 'build/scripts-3.12' does not exist -- can't clean it I: pybuild base:311: python3.13 setup.py clean /usr/lib/python3/dist-packages/setuptools/_distutils/dist.py:261: UserWarning: Unknown distribution option: 'tests_require' warnings.warn(msg) running clean removing '/build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build' (and everything under it) 'build/bdist.linux-armv7l' does not exist -- can't clean it 'build/scripts-3.13' does not exist -- can't clean it rm -rf .pybuild/ find . -name \*.pyc -exec rm {} \; dh_autoreconf_clean -O--buildsystem=pybuild dh_clean -O--buildsystem=pybuild rm -f debian/debhelper-build-stamp rm -rf debian/.debhelper/ rm -f -- debian/errbot.substvars debian/files rm -fr -- debian/errbot/ debian/tmp/ find . \( \( \ \( -path .\*/.git -o -path .\*/.svn -o -path .\*/.bzr -o -path .\*/.hg -o -path .\*/CVS -o -path .\*/.pc -o -path .\*/_darcs \) -prune -o -type f -a \ \( -name '#*#' -o -name '.*~' -o -name '*~' -o -name DEADJOE \ -o -name '*.orig' -o -name '*.rej' -o -name '*.bak' \ -o -name '.*.orig' -o -name .*.rej -o -name '.SUMS' \ -o -name TAGS -o \( -path '*/.deps/*' -a -name '*.P' \) \ \) -exec rm -f {} + \) -o \ \( -type d -a \( -name autom4te.cache -o -name __pycache__ \) -prune -exec rm -rf {} + \) \) debian/rules binary dh binary --buildsystem=pybuild dh_update_autotools_config -O--buildsystem=pybuild dh_autoreconf -O--buildsystem=pybuild dh_auto_configure -O--buildsystem=pybuild pybuild --configure -i python{version} -p "3.12 3.13" I: pybuild base:311: python3.12 setup.py config /usr/lib/python3/dist-packages/setuptools/_distutils/dist.py:261: UserWarning: Unknown distribution option: 'tests_require' warnings.warn(msg) running config I: pybuild base:311: python3.13 setup.py config /usr/lib/python3/dist-packages/setuptools/_distutils/dist.py:261: UserWarning: Unknown distribution option: 'tests_require' warnings.warn(msg) running config dh_auto_build -O--buildsystem=pybuild pybuild --build -i python{version} -p "3.12 3.13" I: pybuild base:311: /usr/bin/python3.12 setup.py build /usr/lib/python3/dist-packages/setuptools/_distutils/dist.py:261: UserWarning: Unknown distribution option: 'tests_require' warnings.warn(msg) running build running build_py creating /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot copying ./errbot/plugin_manager.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot copying ./errbot/utils.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot copying ./errbot/flow.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot copying ./errbot/bootstrap.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot copying ./errbot/streaming.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot copying ./errbot/repo_manager.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot copying ./errbot/config-template.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot copying ./errbot/version.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot copying ./errbot/plugin_wizard.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot copying ./errbot/logs.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot copying ./errbot/botplugin.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot copying ./errbot/core.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot copying ./errbot/backend_plugin_manager.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot copying ./errbot/__init__.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot copying ./errbot/cli.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot copying ./errbot/plugin_info.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot copying ./errbot/templating.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot creating /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/storage copying ./errbot/storage/base.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/storage copying ./errbot/storage/memory.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/storage copying ./errbot/storage/__init__.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/storage copying ./errbot/storage/shelf.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/storage creating /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/rendering copying ./errbot/rendering/xhtmlim.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/rendering copying ./errbot/rendering/ansiext.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/rendering copying ./errbot/rendering/__init__.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/rendering creating /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/backends copying ./errbot/backends/base.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/backends copying ./errbot/backends/text.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/backends copying ./errbot/backends/irc.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/backends copying ./errbot/backends/test.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/backends copying ./errbot/backends/__init__.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/backends copying ./errbot/backends/null.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/backends copying ./errbot/backends/telegram_messenger.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/backends copying ./errbot/backends/xmpp.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/backends creating /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/utils.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/cnf_filter.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/textcmds.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/flows.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/webserver.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/acls.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/plugins.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/wsview.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/health.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/help.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/vcheck.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/chatRoom.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/__init__.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/backup.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/core_plugins copying ./errbot/backends/null.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/backends copying ./errbot/backends/test.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/backends copying ./errbot/backends/xmpp.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/backends copying ./errbot/backends/telegram_messenger.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/backends copying ./errbot/backends/text.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/backends copying ./errbot/backends/irc.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/backends creating /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/backends/styles copying ./errbot/backends/styles/style.css -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/backends/styles copying ./errbot/backends/styles/style-demo.css -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/backends/styles creating /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/backends/images copying ./errbot/backends/images/errbot-bg.svg -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/backends/images copying ./errbot/backends/images/prompt.svg -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/backends/images copying ./errbot/backends/images/errbot.svg -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/backends/images copying ./errbot/core_plugins/cnf_filter.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/backup.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/vcheck.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/health.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/webserver.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/utils.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/flows.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/help.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/acls.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/textcmds.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/plugins.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/chatRoom.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/test.md -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/core_plugins creating /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/core_plugins/templates copying ./errbot/core_plugins/templates/status.md -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/core_plugins/templates copying ./errbot/core_plugins/templates/webstatus.md -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/core_plugins/templates copying ./errbot/core_plugins/templates/status_gc.md -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/core_plugins/templates copying ./errbot/core_plugins/templates/status_plugins.md -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/core_plugins/templates copying ./errbot/core_plugins/templates/plugin_info.md -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/core_plugins/templates copying ./errbot/core_plugins/templates/repos2.md -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/core_plugins/templates copying ./errbot/core_plugins/templates/repos.md -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/core_plugins/templates copying ./errbot/core_plugins/templates/about.md -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/core_plugins/templates copying ./errbot/core_plugins/templates/status_load.md -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/core_plugins/templates copying ./errbot/core_plugins/templates/webserver.md -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/core_plugins/templates copying ./errbot/storage/shelf.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/storage copying ./errbot/storage/memory.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/storage creating /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/templates/initdir copying ./errbot/templates/initdir/example.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/templates/initdir copying ./errbot/templates/initdir/example.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/templates/initdir copying ./errbot/templates/initdir/config.py.tmpl -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/templates/initdir copying ./errbot/templates/card.md -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/templates copying ./errbot/templates/new_plugin.py.tmpl -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/templates I: pybuild base:311: /usr/bin/python3 setup.py build /usr/lib/python3/dist-packages/setuptools/_distutils/dist.py:261: UserWarning: Unknown distribution option: 'tests_require' warnings.warn(msg) running build running build_py creating /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot copying ./errbot/plugin_manager.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot copying ./errbot/utils.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot copying ./errbot/flow.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot copying ./errbot/bootstrap.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot copying ./errbot/streaming.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot copying ./errbot/repo_manager.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot copying ./errbot/config-template.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot copying ./errbot/version.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot copying ./errbot/plugin_wizard.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot copying ./errbot/logs.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot copying ./errbot/botplugin.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot copying ./errbot/core.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot copying ./errbot/backend_plugin_manager.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot copying ./errbot/__init__.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot copying ./errbot/cli.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot copying ./errbot/plugin_info.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot copying ./errbot/templating.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot creating /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/storage copying ./errbot/storage/base.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/storage copying ./errbot/storage/memory.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/storage copying ./errbot/storage/__init__.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/storage copying ./errbot/storage/shelf.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/storage creating /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/rendering copying ./errbot/rendering/xhtmlim.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/rendering copying ./errbot/rendering/ansiext.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/rendering copying ./errbot/rendering/__init__.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/rendering creating /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/backends copying ./errbot/backends/base.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/backends copying ./errbot/backends/text.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/backends copying ./errbot/backends/irc.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/backends copying ./errbot/backends/test.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/backends copying ./errbot/backends/__init__.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/backends copying ./errbot/backends/null.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/backends copying ./errbot/backends/telegram_messenger.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/backends copying ./errbot/backends/xmpp.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/backends creating /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/utils.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/cnf_filter.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/textcmds.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/flows.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/webserver.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/acls.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/plugins.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/wsview.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/health.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/help.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/vcheck.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/chatRoom.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/__init__.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/backup.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/core_plugins copying ./errbot/backends/null.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/backends copying ./errbot/backends/test.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/backends copying ./errbot/backends/xmpp.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/backends copying ./errbot/backends/telegram_messenger.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/backends copying ./errbot/backends/text.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/backends copying ./errbot/backends/irc.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/backends creating /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/backends/styles copying ./errbot/backends/styles/style.css -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/backends/styles copying ./errbot/backends/styles/style-demo.css -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/backends/styles creating /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/backends/images copying ./errbot/backends/images/errbot-bg.svg -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/backends/images copying ./errbot/backends/images/prompt.svg -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/backends/images copying ./errbot/backends/images/errbot.svg -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/backends/images copying ./errbot/core_plugins/cnf_filter.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/backup.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/vcheck.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/health.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/webserver.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/utils.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/flows.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/help.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/acls.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/textcmds.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/plugins.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/chatRoom.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/test.md -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/core_plugins creating /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/core_plugins/templates copying ./errbot/core_plugins/templates/status.md -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/core_plugins/templates copying ./errbot/core_plugins/templates/webstatus.md -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/core_plugins/templates copying ./errbot/core_plugins/templates/status_gc.md -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/core_plugins/templates copying ./errbot/core_plugins/templates/status_plugins.md -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/core_plugins/templates copying ./errbot/core_plugins/templates/plugin_info.md -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/core_plugins/templates copying ./errbot/core_plugins/templates/repos2.md -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/core_plugins/templates copying ./errbot/core_plugins/templates/repos.md -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/core_plugins/templates copying ./errbot/core_plugins/templates/about.md -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/core_plugins/templates copying ./errbot/core_plugins/templates/status_load.md -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/core_plugins/templates copying ./errbot/core_plugins/templates/webserver.md -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/core_plugins/templates copying ./errbot/storage/shelf.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/storage copying ./errbot/storage/memory.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/storage creating /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/templates/initdir copying ./errbot/templates/initdir/example.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/templates/initdir copying ./errbot/templates/initdir/example.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/templates/initdir copying ./errbot/templates/initdir/config.py.tmpl -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/templates/initdir copying ./errbot/templates/card.md -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/templates copying ./errbot/templates/new_plugin.py.tmpl -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/templates debian/rules override_dh_auto_test make[1]: Entering directory '/build/reproducible-path/errbot-6.2.0+ds' localehelper LANG=en_US.UTF-8 dh_auto_test pybuild --test --test-pytest -i python{version} -p "3.12 3.13" I: pybuild pybuild:308: rm -f /build/reproducible-path/errbot-6.2.0+ds/tests/backend_tests/slack_test.py I: pybuild base:311: cd /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build; python3.12 -m pytest -k "not test_broken_plugin and not test_backup and not test_plugin_cycle and not test_entrypoint_paths and not test_check_dependencies_requirements_file_all_installed" ============================= test session starts ============================== platform linux -- Python 3.12.8, pytest-8.3.4, pluggy-1.5.0 rootdir: /build/reproducible-path/errbot-6.2.0+ds plugins: typeguard-4.4.1 collected 215 items / 8 deselected / 207 selected tests/backend_manager_test.py ... [ 1%] tests/backend_tests/text_test.py . [ 1%] tests/base_backend_test.py ................................. [ 17%] tests/cascade_dependencies_test.py . [ 18%] tests/circular_dependencies_test.py . [ 18%] tests/commands_test.py ..EEEEEEEEEEEEEEEEEEEEEEEEEE [ 32%] tests/core_plugins_test.py EEE [ 33%] tests/core_test.py EE [ 34%] tests/dependencies_test.py EEEEEEE [ 38%] tests/dynaplug_test.py EEEEE [ 40%] tests/flow_e2e_test.py EEEEEEEEEEEEE [ 46%] tests/flow_test.py ... [ 48%] tests/i18n_test.py EEEE [ 50%] tests/link_test.py E [ 50%] tests/matchall_test.py EE [ 51%] tests/md_rendering_test.py .... [ 53%] tests/mention_test.py EEE [ 55%] tests/muc_test.py EEEEEE [ 57%] tests/multi_plugin_test.py EE [ 58%] tests/persistence_test.py .. [ 59%] tests/plugin_config_fail_test.py E [ 60%] tests/plugin_config_test.py .......E [ 64%] tests/plugin_info_test.py ........ [ 68%] tests/plugin_management_test.py ....... [ 71%] tests/poller_test.py EE [ 72%] tests/repo_manager_test.py ....... [ 75%] tests/simple_identifiers_test.py ..... [ 78%] tests/streaming_test.py F [ 78%] tests/syntax_test.py EEEE [ 80%] tests/templates_test.py EEEEE [ 83%] tests/utils_test.py .................... [ 92%] tests/webhooks_test.py EEEEEEEEEEEEEEE [100%] ==================================== ERRORS ==================================== _________________________ ERROR at setup of test_about _________________________ self = , processes = 10 initializer = None, initargs = (), maxtasksperchild = None, context = None def __init__(self, processes=None, initializer=None, initargs=(), maxtasksperchild=None, context=None): # Attributes initialized early to make sure that they exist in # __del__() if __init__() raises an exception self._pool = [] self._state = INIT self._ctx = context or get_context() self._setup_queues() self._taskqueue = queue.SimpleQueue() # The _change_notifier queue exist to wake up self._handle_workers() # when the cache (self._cache) is empty or when there is a change in # the _state variable of the thread that runs _handle_workers. self._change_notifier = self._ctx.SimpleQueue() self._cache = _PoolCache(notifier=self._change_notifier) self._maxtasksperchild = maxtasksperchild self._initializer = initializer self._initargs = initargs if processes is None: processes = os.cpu_count() or 1 if processes < 1: raise ValueError("Number of processes must be at least 1") if maxtasksperchild is not None: if not isinstance(maxtasksperchild, int) or maxtasksperchild <= 0: raise ValueError("maxtasksperchild must be a positive int or None") if initializer is not None and not callable(initializer): raise TypeError('initializer must be a callable') self._processes = processes try: > self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:215: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = , processes = 10 initializer = None, initargs = (), maxtasksperchild = None, context = None def __init__(self, processes=None, initializer=None, initargs=(), maxtasksperchild=None, context=None): # Attributes initialized early to make sure that they exist in # __del__() if __init__() raises an exception self._pool = [] self._state = INIT self._ctx = context or get_context() self._setup_queues() self._taskqueue = queue.SimpleQueue() # The _change_notifier queue exist to wake up self._handle_workers() # when the cache (self._cache) is empty or when there is a change in # the _state variable of the thread that runs _handle_workers. self._change_notifier = self._ctx.SimpleQueue() self._cache = _PoolCache(notifier=self._change_notifier) self._maxtasksperchild = maxtasksperchild self._initializer = initializer self._initargs = initargs if processes is None: processes = os.cpu_count() or 1 if processes < 1: raise ValueError("Number of processes must be at least 1") if maxtasksperchild is not None: if not isinstance(maxtasksperchild, int) or maxtasksperchild <= 0: raise ValueError("maxtasksperchild must be a positive int or None") if initializer is not None and not callable(initializer): raise TypeError('initializer must be a callable') self._processes = processes try: self._repopulate_pool() except Exception: for p in self._pool: if p.exitcode is None: > p.terminate() E AttributeError: 'DummyProcess' object has no attribute 'terminate' /usr/lib/python3.12/multiprocessing/pool.py:219: AttributeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread During handling of the above exception, another exception occurred: Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 219, in __init__ p.terminate() ^^^^^^^^^^^ AttributeError: 'DummyProcess' object has no attribute 'terminate' ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:06,767 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:06,771 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:06,771 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:06,774 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:06,775 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:06,776 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread During handling of the above exception, another exception occurred: Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 219, in __init__ p.terminate() ^^^^^^^^^^^ AttributeError: 'DummyProcess' object has no attribute 'terminate' ________________________ ERROR at setup of test_uptime _________________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:07,128 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:07,131 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:07,131 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:07,134 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:07,134 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:07,135 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ________________________ ERROR at setup of test_status _________________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:07,397 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:07,400 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:07,400 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:07,403 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:07,403 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:07,404 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ____________________ ERROR at setup of test_status_plugins _____________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:07,658 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:07,661 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:07,662 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:07,664 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:07,665 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:07,666 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ______________________ ERROR at setup of test_status_load ______________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:07,922 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:07,925 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:07,925 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:07,928 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:07,928 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:07,929 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ________________________ ERROR at setup of test_whoami _________________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:08,188 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:08,192 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:08,192 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:08,195 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:08,195 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:08,196 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _________________________ ERROR at setup of test_echo __________________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:08,453 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:08,456 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:08,457 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:08,460 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:08,460 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:08,461 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _______________________ ERROR at setup of test_status_gc _______________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:08,719 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:08,722 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:08,722 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:08,725 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:08,725 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:08,726 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _____________________ ERROR at setup of test_config_cycle ______________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:08,991 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:08,995 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:08,995 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:08,998 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:08,999 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:08,999 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ________________________ ERROR at setup of test_apropos ________________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:09,261 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:09,265 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:09,265 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:09,268 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:09,268 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:09,269 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ________________________ ERROR at setup of test_logtail ________________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:09,533 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:09,537 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:09,537 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:09,540 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:09,540 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:09,541 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ________________________ ERROR at setup of test_history ________________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:09,807 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:09,810 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:09,811 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:09,814 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:09,814 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:09,815 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _________________ ERROR at setup of test_encoding_preservation _________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:10,071 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:10,074 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:10,074 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:10,077 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:10,078 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:10,078 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ________________ ERROR at setup of test_webserver_webhook_test _________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:10,336 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:10,339 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:10,340 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:10,343 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:10,343 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:10,344 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ____________ ERROR at setup of test_activate_reload_and_deactivate _____________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:10,697 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:10,700 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:10,701 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:10,704 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:10,704 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:10,705 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _______________ ERROR at setup of test_unblacklist_and_blacklist _______________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:10,964 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:10,968 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:10,968 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:10,971 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:10,972 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:10,972 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ____________________ ERROR at setup of test_optional_prefix ____________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:11,239 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:11,242 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:11,243 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:11,246 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:11,246 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:11,247 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ________________ ERROR at setup of test_optional_prefix_re_cmd _________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:11,516 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:11,519 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:11,520 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:11,523 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:11,523 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:11,524 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _____________________ ERROR at setup of test_simple_match ______________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:11,785 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:11,788 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:11,788 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:11,791 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:11,792 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:11,793 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _______________ ERROR at setup of test_no_suggest_on_re_commands _______________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:12,054 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:12,058 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:12,058 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:12,061 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:12,061 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:12,062 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread __________________ ERROR at setup of test_callback_no_command __________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:12,340 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:12,343 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:12,344 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:12,347 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:12,347 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:12,348 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ______________________ ERROR at setup of test_subcommands ______________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:12,608 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:12,612 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:12,612 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:12,615 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:12,616 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:12,616 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ______ ERROR at setup of test_command_not_found_with_space_in_bot_prefix _______ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:12,880 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:12,883 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:12,884 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:12,887 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:12,887 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:12,888 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ____________________ ERROR at setup of test_mock_injection _____________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:13,155 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:13,158 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:13,158 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:13,162 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:13,162 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:13,163 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ___________________ ERROR at setup of test_multiline_command ___________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:13,422 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:13,425 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:13,426 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:13,429 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:13,429 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:13,430 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread __________________ ERROR at setup of test_plugin_info_command __________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:13,695 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:13,699 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:13,699 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:13,703 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:13,703 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:13,704 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread __________________ ERROR at setup of test_help_is_still_here ___________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- DEBUG errbot.backends.test Merging {'CORE_PLUGINS': ('Help', 'Utils', 'CommandNotFoundFilter'), 'BOT_ALT_PREFIXES': ('!',), 'BOT_PREFIX': '$'} to the bot config. INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:13,967 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:13,970 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:13,970 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:13,974 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:13,974 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:13,975 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ____________________ ERROR at setup of test_echo_still_here ____________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- DEBUG errbot.backends.test Merging {'CORE_PLUGINS': ('Help', 'Utils', 'CommandNotFoundFilter'), 'BOT_ALT_PREFIXES': ('!',), 'BOT_PREFIX': '$'} to the bot config. INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:14,238 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:14,241 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:14,241 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:14,245 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:14,245 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:14,246 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread __________________ ERROR at setup of test_bot_prefix_replaced __________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- DEBUG errbot.backends.test Merging {'CORE_PLUGINS': ('Help', 'Utils', 'CommandNotFoundFilter'), 'BOT_ALT_PREFIXES': ('!',), 'BOT_PREFIX': '$'} to the bot config. INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:14,507 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:14,511 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:14,511 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:14,514 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:14,515 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:14,515 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ___________________ ERROR at setup of test_admins_to_notify ____________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- DEBUG errbot.backends.test Merging {'BOT_ADMINS_NOTIFICATIONS': 'zoni@localdomain'} to the bot config. INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:14,782 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:14,786 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:14,786 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:14,790 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:14,790 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:14,791 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread __________________ ERROR at setup of test_admins_not_notified __________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- DEBUG errbot.backends.test Merging {'BOT_ADMINS_NOTIFICATIONS': 'zoni@localdomain'} to the bot config. INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:15,045 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:15,048 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:15,049 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:15,052 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:15,053 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:15,054 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _______________ ERROR at setup of test_if_all_loaded_by_default ________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:15,402 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:15,405 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:15,405 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:15,409 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:15,409 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:15,410 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ___________________ ERROR at setup of test_single_dependency ___________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:15,675 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:15,678 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:15,678 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:15,682 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:15,682 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:15,683 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ___________________ ERROR at setup of test_double_dependency ___________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:15,940 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:15,943 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:15,943 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:15,948 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:15,948 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:15,949 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _________________ ERROR at setup of test_dependency_retrieval __________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:16,209 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:16,213 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:16,213 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:16,217 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:16,217 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:16,218 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ______________ ERROR at setup of test_direct_circular_dependency _______________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:16,478 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:16,481 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:16,481 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:16,485 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:16,485 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:16,486 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _____________ ERROR at setup of test_indirect_circular_dependency ______________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:16,740 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:16,744 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:16,744 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:16,748 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:16,748 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:16,749 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread __________________ ERROR at setup of test_chained_dependency ___________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:17,002 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:17,006 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:17,006 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:17,010 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:17,010 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:17,011 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ________________________ ERROR at setup of test_simple _________________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:17,267 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:17,271 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:17,271 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:17,275 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:17,275 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:17,276 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread __________________________ ERROR at setup of test_arg __________________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:17,538 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:17,542 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:17,542 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:17,546 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:17,546 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:17,547 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread __________________________ ERROR at setup of test_re ___________________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:17,801 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:17,804 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:17,805 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:17,808 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:17,809 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:17,810 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread __________________________ ERROR at setup of test_saw __________________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:18,060 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:18,063 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:18,064 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:18,068 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:18,068 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:18,069 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _______________________ ERROR at setup of test_clashing ________________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:18,321 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:18,324 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:18,325 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:18,329 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:18,329 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:18,330 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ______________________ ERROR at setup of test_list_flows _______________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:18,588 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:18,591 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:18,591 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:18,595 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:18,596 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:18,597 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ____________________ ERROR at setup of test_no_autotrigger _____________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:18,846 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:18,849 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:18,850 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:18,853 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:18,855 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:18,856 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ______________________ ERROR at setup of test_autotrigger ______________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:19,105 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:19,108 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:19,109 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:19,113 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:19,113 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:19,114 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _______________ ERROR at setup of test_no_duplicate_autotrigger ________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:19,461 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:19,465 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:19,465 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:19,469 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:19,469 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:19,470 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _________________ ERROR at setup of test_secondary_autotrigger _________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:19,734 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:19,737 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:19,738 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:19,742 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:19,742 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:19,743 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ______________________ ERROR at setup of test_manual_flow ______________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:19,998 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:20,002 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:20,002 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:20,006 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:20,007 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:20,007 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread __________ ERROR at setup of test_manual_flow_with_or_without_hinting __________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:20,265 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:20,269 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:20,269 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:20,273 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:20,274 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:20,275 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _________________ ERROR at setup of test_no_flyby_trigger_flow _________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:20,529 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:20,532 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:20,532 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:20,536 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:20,537 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:20,538 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _______________________ ERROR at setup of test_flow_only _______________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:20,798 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:20,801 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:20,802 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:20,806 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:20,806 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:20,807 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ____________________ ERROR at setup of test_flow_only_help _____________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:21,052 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:21,055 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:21,056 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:21,060 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:21,060 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:21,061 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ______________________ ERROR at setup of test_flows_stop _______________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:21,305 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:21,309 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:21,309 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:21,313 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:21,314 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:21,314 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ______________________ ERROR at setup of test_flows_kill _______________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:21,563 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:21,566 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:21,566 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:21,570 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:21,571 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:21,572 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _______________________ ERROR at setup of test_room_flow _______________________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:21,824 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:21,828 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:21,828 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:21,832 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:21,832 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:21,833 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ______________________ ERROR at setup of test_i18n_return ______________________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:22,087 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:22,090 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:22,090 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:22,095 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:22,095 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:22,096 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ___________________ ERROR at setup of test_i18n_simple_name ____________________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:22,342 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:22,346 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:22,346 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:22,350 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:22,350 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:22,351 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ______________________ ERROR at setup of test_i18n_prefix ______________________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:22,600 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:22,603 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:22,603 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:22,607 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:22,608 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:22,609 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ______________________ ERROR at setup of test_i18n_suffix ______________________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:22,860 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:22,864 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:22,864 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:22,868 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:22,868 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:22,869 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread __________________ ERROR at setup of test_linked_plugin_here ___________________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:23,113 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:23,116 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:23,117 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:23,121 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:23,121 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:23,122 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ___________________ ERROR at setup of test_botmatch_correct ____________________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:23,476 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:23,479 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:23,480 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:23,484 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:23,485 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:23,485 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _______________________ ERROR at setup of test_botmatch ________________________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:23,740 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:23,743 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:23,744 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:23,748 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:23,748 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:23,749 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ____________________ ERROR at setup of test_foreign_mention ____________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:24,008 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:24,013 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:24,014 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:24,018 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:24,018 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:24,019 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ____________________ ERROR at setup of test_testbot_mention ____________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:24,265 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:24,268 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:24,269 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:24,273 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:24,273 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:24,274 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ___________________ ERROR at setup of test_multiple_mentions ___________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:24,523 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:24,526 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:24,526 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:24,531 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:24,531 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:24,532 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ____________________ ERROR at setup of test_plugin_methods _____________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:24,783 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:24,786 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:24,787 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:24,791 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:24,792 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:24,793 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread __________ ERROR at setup of test_create_join_leave_destroy_lifecycle __________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:25,050 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:25,053 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:25,053 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:25,058 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:25,058 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:25,059 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _______________________ ERROR at setup of test_occupants _______________________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:25,306 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:25,309 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:25,309 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:25,314 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:25,314 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:25,315 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _________________________ ERROR at setup of test_topic _________________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:25,565 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:25,568 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:25,568 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:25,573 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:25,573 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:25,574 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ___________________ ERROR at setup of test_plugin_callbacks ____________________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:25,821 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:25,825 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:25,825 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:25,829 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:25,830 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:25,831 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ______________________ ERROR at setup of test_botcommands ______________________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:26,085 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:26,088 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:26,089 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:26,093 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:26,094 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:26,094 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _________________________ ERROR at setup of test_first _________________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:26,343 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:26,346 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:26,346 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:26,351 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:26,351 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:26,352 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ________________________ ERROR at setup of test_second _________________________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:26,600 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:26,603 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:26,603 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:26,608 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:26,608 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:26,609 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _____________________ ERROR at setup of test_failed_config _____________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:26,864 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:26,867 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:26,868 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:26,872 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:26,872 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:26,873 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _____________________ ERROR at setup of test_failed_config _____________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:27,139 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:27,143 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:27,143 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:27,263 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:27,264 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:27,265 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _____________________ ERROR at setup of test_delayed_hello _____________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:27,589 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:27,592 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:27,593 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:27,598 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:27,600 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:27,601 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread __________________ ERROR at setup of test_delayed_hello_loop ___________________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:27,854 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:27,858 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:27,858 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:27,863 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:27,863 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:27,864 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _______________________ ERROR at setup of test_nosyntax ________________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:28,209 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:28,212 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:28,212 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:28,217 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:28,217 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:28,218 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ________________________ ERROR at setup of test_syntax _________________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:28,472 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:28,475 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:28,476 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:28,480 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:28,481 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:28,481 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _______________________ ERROR at setup of test_re_syntax _______________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:28,736 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:28,739 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:28,739 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:28,744 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:28,744 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:28,745 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ______________________ ERROR at setup of test_arg_syntax _______________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:28,991 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:28,995 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:28,995 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:29,001 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:29,001 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:29,002 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ______________________ ERROR at setup of test_templates_1 ______________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:29,249 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:29,253 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:29,253 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:29,258 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:29,258 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:29,259 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ______________________ ERROR at setup of test_templates_2 ______________________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:29,510 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:29,513 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:29,513 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:29,518 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:29,518 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:29,519 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ______________________ ERROR at setup of test_templates_3 ______________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:29,771 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:29,775 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:29,775 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:29,780 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:29,780 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:29,781 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ______________________ ERROR at setup of test_templates_4 ______________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:30,027 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:30,030 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:30,030 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:30,036 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:30,036 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:30,037 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ______________________ ERROR at setup of test_templates_5 ______________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:30,290 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:30,293 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:30,293 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:30,298 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:30,299 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:30,300 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ____________ ERROR at setup of test_not_configured_url_returns_404 _____________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:30,601 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:30,605 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:30,605 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:30,610 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:30,610 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:30,611 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread __________________ ERROR at setup of test_webserver_plugin_ok __________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:30,870 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:30,874 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:30,874 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:30,879 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:30,880 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:30,880 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _________________ ERROR at setup of test_trailing_no_slash_ok __________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:31,140 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:31,144 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:31,144 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:31,269 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:31,269 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:31,270 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ________________ ERROR at setup of test_trailing_slash_also_ok _________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:31,525 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:31,528 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:31,529 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:31,534 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:31,534 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:31,535 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _____________ ERROR at setup of test_json_is_automatically_decoded _____________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:31,788 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:31,791 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:31,792 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:31,797 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:31,797 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:31,798 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ______ ERROR at setup of test_json_on_custom_url_is_automatically_decoded ______ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:32,050 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:32,053 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:32,053 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:32,058 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:32,058 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:32,059 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _ ERROR at setup of test_post_form_on_webhook_without_form_param_is_automatically_decoded _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:32,305 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:32,308 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:32,309 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:32,314 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:32,315 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:32,315 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _ ERROR at setup of test_post_form_on_webhook_with_custom_url_and_without_form_param_is_automatically_decoded _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:32,561 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:32,565 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:32,565 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:32,570 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:32,570 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:32,571 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _ ERROR at setup of test_webhooks_with_form_parameter_decode_json_automatically _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:32,822 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:32,825 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:32,826 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:32,830 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:32,831 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:32,832 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _ ERROR at setup of test_webhooks_with_form_parameter_on_custom_url_decode_json_automatically _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:33,089 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:33,092 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:33,093 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:33,098 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:33,098 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:33,099 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _______________ ERROR at setup of test_webhooks_with_raw_request _______________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:33,348 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:33,352 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:33,352 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:33,358 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:33,359 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:33,360 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _______ ERROR at setup of test_webhooks_with_naked_decorator_raw_request _______ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:33,616 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:33,619 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:33,620 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:33,625 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:33,625 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:33,626 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _______ ERROR at setup of test_generate_certificate_creates_usable_cert ________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:33,890 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:33,894 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:33,894 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:33,899 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:33,900 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:33,901 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ____________ ERROR at setup of test_custom_headers_and_status_codes ____________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:34,165 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:34,169 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:34,169 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:34,174 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:34,175 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:34,176 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ____________________ ERROR at setup of test_lambda_webhook _____________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-02-02 03:37:34,449 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-02-02 03:37:34,453 INFO errbot.bootstrap Found Backend plugin: Test 2025-02-02 03:37:34,453 DEBUG errbot.storage Opening storage 'repomgr' 2025-02-02 03:37:34,458 DEBUG errbot.core ErrBot init. 2025-02-02 03:37:34,459 DEBUG errbot.backends.base Backend init. 2025-02-02 03:37:34,460 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 994, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread =================================== FAILURES =================================== ________________________________ test_streaming ________________________________ def test_streaming(): canary = b"this is my test" * 1000 source = Stream(TestPerson("gbin@gootz.net"), BytesIO(canary)) clients = [StreamingClient() for _ in range(50)] > Tee(source, clients).run() tests/streaming_test.py:17: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/streaming.py:81: in run thread.start() _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:994: RuntimeError =============================== warnings summary =============================== ../../../errbot/utils.py:13 /build/reproducible-path/errbot-6.2.0+ds/errbot/utils.py:13: DeprecationWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html import pkg_resources .pybuild/cpython3_3.12_errbot/build/tests/cascade_dependencies_test.py::test_dependency_commands /usr/lib/python3/dist-packages/webob/compat.py:5: DeprecationWarning: 'cgi' is deprecated and slated for removal in Python 3.13 from cgi import parse_header -- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html =========================== short test summary info ============================ FAILED tests/streaming_test.py::test_streaming - RuntimeError: can't start ne... ERROR tests/commands_test.py::test_about - SystemExit: -1 ERROR tests/commands_test.py::test_uptime - SystemExit: -1 ERROR tests/commands_test.py::test_status - SystemExit: -1 ERROR tests/commands_test.py::test_status_plugins - SystemExit: -1 ERROR tests/commands_test.py::test_status_load - SystemExit: -1 ERROR tests/commands_test.py::test_whoami - SystemExit: -1 ERROR tests/commands_test.py::test_echo - SystemExit: -1 ERROR tests/commands_test.py::test_status_gc - SystemExit: -1 ERROR tests/commands_test.py::test_config_cycle - SystemExit: -1 ERROR tests/commands_test.py::test_apropos - SystemExit: -1 ERROR tests/commands_test.py::test_logtail - SystemExit: -1 ERROR tests/commands_test.py::test_history - SystemExit: -1 ERROR tests/commands_test.py::test_encoding_preservation - SystemExit: -1 ERROR tests/commands_test.py::test_webserver_webhook_test - SystemExit: -1 ERROR tests/commands_test.py::test_activate_reload_and_deactivate - SystemExi... ERROR tests/commands_test.py::test_unblacklist_and_blacklist - SystemExit: -1 ERROR tests/commands_test.py::test_optional_prefix - SystemExit: -1 ERROR tests/commands_test.py::test_optional_prefix_re_cmd - SystemExit: -1 ERROR tests/commands_test.py::test_simple_match - SystemExit: -1 ERROR tests/commands_test.py::test_no_suggest_on_re_commands - SystemExit: -1 ERROR tests/commands_test.py::test_callback_no_command - SystemExit: -1 ERROR tests/commands_test.py::test_subcommands - SystemExit: -1 ERROR tests/commands_test.py::test_command_not_found_with_space_in_bot_prefix ERROR tests/commands_test.py::test_mock_injection - SystemExit: -1 ERROR tests/commands_test.py::test_multiline_command - SystemExit: -1 ERROR tests/commands_test.py::test_plugin_info_command - SystemExit: -1 ERROR tests/core_plugins_test.py::test_help_is_still_here - SystemExit: -1 ERROR tests/core_plugins_test.py::test_echo_still_here - SystemExit: -1 ERROR tests/core_plugins_test.py::test_bot_prefix_replaced - SystemExit: -1 ERROR tests/core_test.py::test_admins_to_notify - SystemExit: -1 ERROR tests/core_test.py::test_admins_not_notified - SystemExit: -1 ERROR tests/dependencies_test.py::test_if_all_loaded_by_default - SystemExit: -1 ERROR tests/dependencies_test.py::test_single_dependency - SystemExit: -1 ERROR tests/dependencies_test.py::test_double_dependency - SystemExit: -1 ERROR tests/dependencies_test.py::test_dependency_retrieval - SystemExit: -1 ERROR tests/dependencies_test.py::test_direct_circular_dependency - SystemExi... ERROR tests/dependencies_test.py::test_indirect_circular_dependency - SystemE... ERROR tests/dependencies_test.py::test_chained_dependency - SystemExit: -1 ERROR tests/dynaplug_test.py::test_simple - SystemExit: -1 ERROR tests/dynaplug_test.py::test_arg - SystemExit: -1 ERROR tests/dynaplug_test.py::test_re - SystemExit: -1 ERROR tests/dynaplug_test.py::test_saw - SystemExit: -1 ERROR tests/dynaplug_test.py::test_clashing - SystemExit: -1 ERROR tests/flow_e2e_test.py::test_list_flows - SystemExit: -1 ERROR tests/flow_e2e_test.py::test_no_autotrigger - SystemExit: -1 ERROR tests/flow_e2e_test.py::test_autotrigger - SystemExit: -1 ERROR tests/flow_e2e_test.py::test_no_duplicate_autotrigger - SystemExit: -1 ERROR tests/flow_e2e_test.py::test_secondary_autotrigger - SystemExit: -1 ERROR tests/flow_e2e_test.py::test_manual_flow - SystemExit: -1 ERROR tests/flow_e2e_test.py::test_manual_flow_with_or_without_hinting - Syst... ERROR tests/flow_e2e_test.py::test_no_flyby_trigger_flow - SystemExit: -1 ERROR tests/flow_e2e_test.py::test_flow_only - SystemExit: -1 ERROR tests/flow_e2e_test.py::test_flow_only_help - SystemExit: -1 ERROR tests/flow_e2e_test.py::test_flows_stop - SystemExit: -1 ERROR tests/flow_e2e_test.py::test_flows_kill - SystemExit: -1 ERROR tests/flow_e2e_test.py::test_room_flow - SystemExit: -1 ERROR tests/i18n_test.py::test_i18n_return - SystemExit: -1 ERROR tests/i18n_test.py::test_i18n_simple_name - SystemExit: -1 ERROR tests/i18n_test.py::test_i18n_prefix - SystemExit: -1 ERROR tests/i18n_test.py::test_i18n_suffix - SystemExit: -1 ERROR tests/link_test.py::test_linked_plugin_here - SystemExit: -1 ERROR tests/matchall_test.py::test_botmatch_correct - SystemExit: -1 ERROR tests/matchall_test.py::test_botmatch - SystemExit: -1 ERROR tests/mention_test.py::test_foreign_mention - SystemExit: -1 ERROR tests/mention_test.py::test_testbot_mention - SystemExit: -1 ERROR tests/mention_test.py::test_multiple_mentions - SystemExit: -1 ERROR tests/muc_test.py::test_plugin_methods - SystemExit: -1 ERROR tests/muc_test.py::test_create_join_leave_destroy_lifecycle - SystemExi... ERROR tests/muc_test.py::test_occupants - SystemExit: -1 ERROR tests/muc_test.py::test_topic - SystemExit: -1 ERROR tests/muc_test.py::test_plugin_callbacks - SystemExit: -1 ERROR tests/muc_test.py::test_botcommands - SystemExit: -1 ERROR tests/multi_plugin_test.py::test_first - SystemExit: -1 ERROR tests/multi_plugin_test.py::test_second - SystemExit: -1 ERROR tests/plugin_config_fail_test.py::test_failed_config - SystemExit: -1 ERROR tests/plugin_config_test.py::test_failed_config - SystemExit: -1 ERROR tests/poller_test.py::test_delayed_hello - SystemExit: -1 ERROR tests/poller_test.py::test_delayed_hello_loop - SystemExit: -1 ERROR tests/syntax_test.py::test_nosyntax - SystemExit: -1 ERROR tests/syntax_test.py::test_syntax - SystemExit: -1 ERROR tests/syntax_test.py::test_re_syntax - SystemExit: -1 ERROR tests/syntax_test.py::test_arg_syntax - SystemExit: -1 ERROR tests/templates_test.py::test_templates_1 - SystemExit: -1 ERROR tests/templates_test.py::test_templates_2 - SystemExit: -1 ERROR tests/templates_test.py::test_templates_3 - SystemExit: -1 ERROR tests/templates_test.py::test_templates_4 - SystemExit: -1 ERROR tests/templates_test.py::test_templates_5 - SystemExit: -1 ERROR tests/webhooks_test.py::test_not_configured_url_returns_404 - SystemExi... ERROR tests/webhooks_test.py::test_webserver_plugin_ok - SystemExit: -1 ERROR tests/webhooks_test.py::test_trailing_no_slash_ok - SystemExit: -1 ERROR tests/webhooks_test.py::test_trailing_slash_also_ok - SystemExit: -1 ERROR tests/webhooks_test.py::test_json_is_automatically_decoded - SystemExit... ERROR tests/webhooks_test.py::test_json_on_custom_url_is_automatically_decoded ERROR tests/webhooks_test.py::test_post_form_on_webhook_without_form_param_is_automatically_decoded ERROR tests/webhooks_test.py::test_post_form_on_webhook_with_custom_url_and_without_form_param_is_automatically_decoded ERROR tests/webhooks_test.py::test_webhooks_with_form_parameter_decode_json_automatically ERROR tests/webhooks_test.py::test_webhooks_with_form_parameter_on_custom_url_decode_json_automatically ERROR tests/webhooks_test.py::test_webhooks_with_raw_request - SystemExit: -1 ERROR tests/webhooks_test.py::test_webhooks_with_naked_decorator_raw_request ERROR tests/webhooks_test.py::test_generate_certificate_creates_usable_cert ERROR tests/webhooks_test.py::test_custom_headers_and_status_codes - SystemEx... ERROR tests/webhooks_test.py::test_lambda_webhook - SystemExit: -1 ===== 1 failed, 104 passed, 8 deselected, 2 warnings, 102 errors in 32.53s ===== E: pybuild pybuild:389: test: plugin distutils failed with: exit code=1: cd /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build; python3.12 -m pytest -k "not test_broken_plugin and not test_backup and not test_plugin_cycle and not test_entrypoint_paths and not test_check_dependencies_requirements_file_all_installed" I: pybuild pybuild:308: rm -f /build/reproducible-path/errbot-6.2.0+ds/tests/backend_tests/slack_test.py I: pybuild base:311: cd /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build; python3.13 -m pytest -k "not test_broken_plugin and not test_backup and not test_plugin_cycle and not test_entrypoint_paths and not test_check_dependencies_requirements_file_all_installed" ============================= test session starts ============================== platform linux -- Python 3.13.1, pytest-8.3.4, pluggy-1.5.0 rootdir: /build/reproducible-path/errbot-6.2.0+ds plugins: typeguard-4.4.1 collected 215 items / 8 deselected / 207 selected tests/backend_manager_test.py ... [ 1%] tests/backend_tests/text_test.py . [ 1%] tests/base_backend_test.py ................................. [ 17%] tests/cascade_dependencies_test.py . [ 18%] tests/circular_dependencies_test.py . [ 18%] tests/commands_test.py ..EEEEEEEEEEEEEEEEEEEEEEEEEE [ 32%] tests/core_plugins_test.py EEE [ 33%] tests/core_test.py EE [ 34%] tests/dependencies_test.py EEEEEEE [ 38%] tests/dynaplug_test.py EEEEE [ 40%] tests/flow_e2e_test.py EEEEEEEEEEEEE [ 46%] tests/flow_test.py ... [ 48%] tests/i18n_test.py EEEE [ 50%] tests/link_test.py E [ 50%] tests/matchall_test.py EE [ 51%] tests/md_rendering_test.py .... [ 53%] tests/mention_test.py EEE [ 55%] tests/muc_test.py EEEEEE [ 57%] tests/multi_plugin_test.py EE [ 58%] tests/persistence_test.py .. [ 59%] tests/plugin_config_fail_test.py E [ 60%] tests/plugin_config_test.py .......E [ 64%] tests/plugin_info_test.py ........ [ 68%] tests/plugin_management_test.py ....... [ 71%] tests/poller_test.py EE [ 72%] tests/repo_manager_test.py ....... [ 75%] tests/simple_identifiers_test.py ..... [ 78%] tests/streaming_test.py F [ 78%] tests/syntax_test.py EEEE [ 80%] tests/templates_test.py EEEEE [ 83%] tests/utils_test.py .................... [ 92%] tests/webhooks_test.py EEEEEEEEEEE INTERNALERROR> Traceback (most recent call last): INTERNALERROR> File "/usr/lib/python3/dist-packages/_pytest/main.py", line 283, in wrap_session INTERNALERROR> session.exitstatus = doit(config, session) or 0 INTERNALERROR> ~~~~^^^^^^^^^^^^^^^^^ INTERNALERROR> File "/usr/lib/python3/dist-packages/_pytest/main.py", line 337, in _main INTERNALERROR> config.hook.pytest_runtestloop(session=session) INTERNALERROR> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^ INTERNALERROR> File "/usr/lib/python3/dist-packages/pluggy/_hooks.py", line 513, in __call__ INTERNALERROR> return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult) INTERNALERROR> ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ INTERNALERROR> File "/usr/lib/python3/dist-packages/pluggy/_manager.py", line 120, in _hookexec INTERNALERROR> return self._inner_hookexec(hook_name, methods, kwargs, firstresult) INTERNALERROR> ~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ INTERNALERROR> File "/usr/lib/python3/dist-packages/pluggy/_callers.py", line 139, in _multicall INTERNALERROR> raise exception.with_traceback(exception.__traceback__) INTERNALERROR> File "/usr/lib/python3/dist-packages/pluggy/_callers.py", line 122, in _multicall INTERNALERROR> teardown.throw(exception) # type: ignore[union-attr] INTERNALERROR> ~~~~~~~~~~~~~~^^^^^^^^^^^ INTERNALERROR> File "/usr/lib/python3/dist-packages/_pytest/logging.py", line 803, in pytest_runtestloop INTERNALERROR> return (yield) # Run all the tests. INTERNALERROR> ^^^^^ INTERNALERROR> File "/usr/lib/python3/dist-packages/pluggy/_callers.py", line 122, in _multicall INTERNALERROR> teardown.throw(exception) # type: ignore[union-attr] INTERNALERROR> ~~~~~~~~~~~~~~^^^^^^^^^^^ INTERNALERROR> File "/usr/lib/python3/dist-packages/_pytest/terminal.py", line 673, in pytest_runtestloop INTERNALERROR> result = yield INTERNALERROR> ^^^^^ INTERNALERROR> File "/usr/lib/python3/dist-packages/pluggy/_callers.py", line 103, in _multicall INTERNALERROR> res = hook_impl.function(*args) INTERNALERROR> File "/usr/lib/python3/dist-packages/_pytest/main.py", line 362, in pytest_runtestloop INTERNALERROR> item.config.hook.pytest_runtest_protocol(item=item, nextitem=nextitem) INTERNALERROR> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ INTERNALERROR> File "/usr/lib/python3/dist-packages/pluggy/_hooks.py", line 513, in __call__ INTERNALERROR> return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult) INTERNALERROR> ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ INTERNALERROR> File "/usr/lib/python3/dist-packages/pluggy/_manager.py", line 120, in _hookexec INTERNALERROR> return self._inner_hookexec(hook_name, methods, kwargs, firstresult) INTERNALERROR> ~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ INTERNALERROR> File "/usr/lib/python3/dist-packages/pluggy/_callers.py", line 139, in _multicall INTERNALERROR> raise exception.with_traceback(exception.__traceback__) INTERNALERROR> File "/usr/lib/python3/dist-packages/pluggy/_callers.py", line 122, in _multicall INTERNALERROR> teardown.throw(exception) # type: ignore[union-attr] INTERNALERROR> ~~~~~~~~~~~~~~^^^^^^^^^^^ INTERNALERROR> File "/usr/lib/python3/dist-packages/_pytest/warnings.py", line 112, in pytest_runtest_protocol INTERNALERROR> return (yield) INTERNALERROR> ^^^^^ INTERNALERROR> File "/usr/lib/python3/dist-packages/pluggy/_callers.py", line 122, in _multicall INTERNALERROR> teardown.throw(exception) # type: ignore[union-attr] INTERNALERROR> ~~~~~~~~~~~~~~^^^^^^^^^^^ INTERNALERROR> File "/usr/lib/python3/dist-packages/_pytest/assertion/__init__.py", line 176, in pytest_runtest_protocol INTERNALERROR> return (yield) INTERNALERROR> ^^^^^ INTERNALERROR> File "/usr/lib/python3/dist-packages/pluggy/_callers.py", line 122, in _multicall INTERNALERROR> teardown.throw(exception) # type: ignore[union-attr] INTERNALERROR> ~~~~~~~~~~~~~~^^^^^^^^^^^ INTERNALERROR> File "/usr/lib/python3/dist-packages/_pytest/unittest.py", line 429, in pytest_runtest_protocol INTERNALERROR> res = yield INTERNALERROR> ^^^^^ INTERNALERROR> File "/usr/lib/python3/dist-packages/pluggy/_callers.py", line 122, in _multicall INTERNALERROR> teardown.throw(exception) # type: ignore[union-attr] INTERNALERROR> ~~~~~~~~~~~~~~^^^^^^^^^^^ INTERNALERROR> File "/usr/lib/python3/dist-packages/_pytest/faulthandler.py", line 88, in pytest_runtest_protocol INTERNALERROR> return (yield) INTERNALERROR> ^^^^^ INTERNALERROR> File "/usr/lib/python3/dist-packages/pluggy/_callers.py", line 103, in _multicall INTERNALERROR> res = hook_impl.function(*args) INTERNALERROR> File "/usr/lib/python3/dist-packages/_pytest/runner.py", line 113, in pytest_runtest_protocol INTERNALERROR> runtestprotocol(item, nextitem=nextitem) INTERNALERROR> ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ INTERNALERROR> File "/usr/lib/python3/dist-packages/_pytest/runner.py", line 126, in runtestprotocol INTERNALERROR> rep = call_and_report(item, "setup", log) INTERNALERROR> File "/usr/lib/python3/dist-packages/_pytest/runner.py", line 244, in call_and_report INTERNALERROR> report: TestReport = ihook.pytest_runtest_makereport(item=item, call=call) INTERNALERROR> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^ INTERNALERROR> File "/usr/lib/python3/dist-packages/pluggy/_hooks.py", line 513, in __call__ INTERNALERROR> return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult) INTERNALERROR> ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ INTERNALERROR> File "/usr/lib/python3/dist-packages/pluggy/_manager.py", line 120, in _hookexec INTERNALERROR> return self._inner_hookexec(hook_name, methods, kwargs, firstresult) INTERNALERROR> ~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ INTERNALERROR> File "/usr/lib/python3/dist-packages/pluggy/_callers.py", line 139, in _multicall INTERNALERROR> raise exception.with_traceback(exception.__traceback__) INTERNALERROR> File "/usr/lib/python3/dist-packages/pluggy/_callers.py", line 122, in _multicall INTERNALERROR> teardown.throw(exception) # type: ignore[union-attr] INTERNALERROR> ~~~~~~~~~~~~~~^^^^^^^^^^^ INTERNALERROR> File "/usr/lib/python3/dist-packages/_pytest/tmpdir.py", line 310, in pytest_runtest_makereport INTERNALERROR> rep = yield INTERNALERROR> ^^^^^ INTERNALERROR> File "/usr/lib/python3/dist-packages/pluggy/_callers.py", line 122, in _multicall INTERNALERROR> teardown.throw(exception) # type: ignore[union-attr] INTERNALERROR> ~~~~~~~~~~~~~~^^^^^^^^^^^ INTERNALERROR> File "/usr/lib/python3/dist-packages/_pytest/skipping.py", line 269, in pytest_runtest_makereport INTERNALERROR> rep = yield INTERNALERROR> ^^^^^ INTERNALERROR> File "/usr/lib/python3/dist-packages/pluggy/_callers.py", line 103, in _multicall INTERNALERROR> res = hook_impl.function(*args) INTERNALERROR> File "/usr/lib/python3/dist-packages/_pytest/runner.py", line 368, in pytest_runtest_makereport INTERNALERROR> return TestReport.from_item_and_call(item, call) INTERNALERROR> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^ INTERNALERROR> File "/usr/lib/python3/dist-packages/_pytest/reports.py", line 378, in from_item_and_call INTERNALERROR> longrepr = item._repr_failure_py( INTERNALERROR> excinfo, style=item.config.getoption("tbstyle", "auto") INTERNALERROR> ) INTERNALERROR> File "/usr/lib/python3/dist-packages/_pytest/nodes.py", line 456, in _repr_failure_py INTERNALERROR> return excinfo.getrepr( INTERNALERROR> ~~~~~~~~~~~~~~~^ INTERNALERROR> funcargs=True, INTERNALERROR> ^^^^^^^^^^^^^^ INTERNALERROR> ...<5 lines>... INTERNALERROR> truncate_args=truncate_args, INTERNALERROR> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ INTERNALERROR> ) INTERNALERROR> ^ INTERNALERROR> File "/usr/lib/python3/dist-packages/_pytest/_code/code.py", line 690, in getrepr INTERNALERROR> return fmt.repr_excinfo(self) INTERNALERROR> ~~~~~~~~~~~~~~~~^^^^^^ INTERNALERROR> File "/usr/lib/python3/dist-packages/_pytest/_code/code.py", line 1058, in repr_excinfo INTERNALERROR> reprtraceback = self.repr_traceback(excinfo_) INTERNALERROR> File "/usr/lib/python3/dist-packages/_pytest/_code/code.py", line 992, in repr_traceback INTERNALERROR> self.repr_traceback_entry(entry, excinfo if last == entry else None) INTERNALERROR> ~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ INTERNALERROR> File "/usr/lib/python3/dist-packages/_pytest/_code/code.py", line 930, in repr_traceback_entry INTERNALERROR> source = self._getentrysource(entry) INTERNALERROR> File "/usr/lib/python3/dist-packages/_pytest/_code/code.py", line 828, in _getentrysource INTERNALERROR> source = entry.getsource(self.astcache) INTERNALERROR> File "/usr/lib/python3/dist-packages/_pytest/_code/code.py", line 262, in getsource INTERNALERROR> astnode, _, end = getstatementrange_ast( INTERNALERROR> ~~~~~~~~~~~~~~~~~~~~~^ INTERNALERROR> self.lineno, source, astnode=astnode INTERNALERROR> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ INTERNALERROR> ) INTERNALERROR> ^ INTERNALERROR> File "/usr/lib/python3/dist-packages/_pytest/_code/source.py", line 181, in getstatementrange_ast INTERNALERROR> astnode = ast.parse(content, "source", "exec") INTERNALERROR> File "/usr/lib/python3.13/ast.py", line 54, in parse INTERNALERROR> return compile(source, filename, mode, flags, INTERNALERROR> _feature_version=feature_version, optimize=optimize) INTERNALERROR> MemoryError ====== 1 failed, 104 passed, 8 deselected, 1 warning, 98 errors in 27.44s ====== E: pybuild pybuild:389: test: plugin distutils failed with: exit code=3: cd /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build; python3.13 -m pytest -k "not test_broken_plugin and not test_backup and not test_plugin_cycle and not test_entrypoint_paths and not test_check_dependencies_requirements_file_all_installed" rm -fr -- /tmp/dh-xdg-rundir-RtDouc2Q dh_auto_test: error: pybuild --test --test-pytest -i python{version} -p "3.12 3.13" returned exit code 13 make[1]: *** [debian/rules:24: override_dh_auto_test] Error 25 make[1]: Leaving directory '/build/reproducible-path/errbot-6.2.0+ds' make: *** [debian/rules:17: binary] Error 2 dpkg-buildpackage: error: debian/rules binary subprocess returned exit status 2 I: copying local configuration E: Failed autobuilding of package I: unmounting dev/ptmx filesystem I: unmounting dev/pts filesystem I: unmounting dev/shm filesystem I: unmounting proc filesystem I: unmounting sys filesystem I: cleaning the build env I: removing directory /srv/workspace/pbuilder/24190 and its subdirectories Sun Feb 2 15:38:17 UTC 2025 W: No second build log, what happened?