Wed Jan 29 19:50:25 UTC 2025 I: starting to build errbot/unstable/armhf on jenkins on '2025-01-29 19:50' Wed Jan 29 19:50:25 UTC 2025 I: The jenkins build log is/was available at https://jenkins.debian.net/userContent/reproducible/debian/build_service/armhf_4/12589/console.log Wed Jan 29 19:50:25 UTC 2025 I: Downloading source for unstable/errbot=6.2.0+ds-4 --2025-01-29 19:50:26-- 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% 353M=0s 2025-01-29 19:50:26 (353 MB/s) - ‘errbot_6.2.0+ds-4.dsc’ saved [2233/2233] Wed Jan 29 19:50:26 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 <team+python@tracker.debian.org> Uploaders: Birger Schacht <birger@debian.org> 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----- Wed Jan 29 19:50:26 UTC 2025 I: Checking whether the package is not for us Wed Jan 29 19:50:26 UTC 2025 I: Starting 1st build on remote node virt64z-armhf-rb.debian.net. Wed Jan 29 19:50:26 UTC 2025 I: Preparing to do remote build '1' on virt64z-armhf-rb.debian.net. Wed Jan 29 19:54:53 UTC 2025 I: Deleting $TMPDIR on virt64z-armhf-rb.debian.net. I: pbuilder: network access will be disabled during build I: Current time: Wed Jan 29 07:50:32 -12 2025 I: pbuilder-time-stamp: 1738180232 I: Building the build Environment I: extracting base tarball [/var/cache/pbuilder/unstable-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/27481/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='unstable' HOME='/root' HOST_ARCH='armhf' IFS=' ' INVOCATION_ID='ee825067eb9c47a4bbe7326bc5f813ac' 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='27481' 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.j6Exn5DG/pbuilderrc_1sVU --distribution unstable --hookdir /etc/pbuilder/first-build-hooks --debbuildopts -b --basetgz /var/cache/pbuilder/unstable-reproducible-base.tgz --buildresult /srv/reproducible-results/rbuild-debian/r-b-build.j6Exn5DG/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 virt64z 6.1.0-30-arm64 #1 SMP Debian 6.1.124-1 (2025-01-12) aarch64 GNU/Linux I: ls -l /bin lrwxrwxrwx 1 root root 7 Nov 22 14:40 /bin -> usr/bin I: user script /srv/workspace/pbuilder/27481/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 <pbuilder-maint@lists.alioth.debian.org> 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 ... 19568 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 unstable/main armhf libpython3.13-minimal armhf 3.13.1-3 [844 kB] Get: 2 http://deb.debian.org/debian unstable/main armhf libexpat1 armhf 2.6.4-1 [83.5 kB] Get: 3 http://deb.debian.org/debian unstable/main armhf python3.13-minimal armhf 3.13.1-3 [1840 kB] Get: 4 http://deb.debian.org/debian unstable/main armhf python3-minimal armhf 3.13.1-2 [27.0 kB] Get: 5 http://deb.debian.org/debian unstable/main armhf media-types all 10.1.0 [26.9 kB] Get: 6 http://deb.debian.org/debian unstable/main armhf netbase all 6.4 [12.8 kB] Get: 7 http://deb.debian.org/debian unstable/main armhf tzdata all 2025a-1 [259 kB] Get: 8 http://deb.debian.org/debian unstable/main armhf libffi8 armhf 3.4.6-1 [20.0 kB] Get: 9 http://deb.debian.org/debian unstable/main armhf readline-common all 8.2-6 [69.4 kB] Get: 10 http://deb.debian.org/debian unstable/main armhf libreadline8t64 armhf 8.2-6 [146 kB] Get: 11 http://deb.debian.org/debian unstable/main armhf libpython3.13-stdlib armhf 3.13.1-3 [1850 kB] Get: 12 http://deb.debian.org/debian unstable/main armhf python3.13 armhf 3.13.1-3 [740 kB] Get: 13 http://deb.debian.org/debian unstable/main armhf libpython3-stdlib armhf 3.13.1-2 [9952 B] Get: 14 http://deb.debian.org/debian unstable/main armhf python3 armhf 3.13.1-2 [28.0 kB] Get: 15 http://deb.debian.org/debian unstable/main armhf libpython3.12-minimal armhf 3.12.8-5 [803 kB] Get: 16 http://deb.debian.org/debian unstable/main armhf python3.12-minimal armhf 3.12.8-5 [1812 kB] Get: 17 http://deb.debian.org/debian unstable/main armhf sensible-utils all 0.0.24 [24.8 kB] Get: 18 http://deb.debian.org/debian unstable/main armhf openssl armhf 3.4.0-2 [1388 kB] Get: 19 http://deb.debian.org/debian unstable/main armhf ca-certificates all 20241223 [164 kB] Get: 20 http://deb.debian.org/debian unstable/main armhf libmagic-mgc armhf 1:5.45-3+b1 [314 kB] Get: 21 http://deb.debian.org/debian unstable/main armhf libmagic1t64 armhf 1:5.45-3+b1 [98.5 kB] Get: 22 http://deb.debian.org/debian unstable/main armhf file armhf 1:5.45-3+b1 [42.3 kB] Get: 23 http://deb.debian.org/debian unstable/main armhf gettext-base armhf 0.23.1-1 [238 kB] Get: 24 http://deb.debian.org/debian unstable/main armhf libuchardet0 armhf 0.0.8-1+b2 [65.6 kB] Get: 25 http://deb.debian.org/debian unstable/main armhf groff-base armhf 1.23.0-7 [1095 kB] Get: 26 http://deb.debian.org/debian unstable/main armhf locales all 2.40-6 [3901 kB] Get: 27 http://deb.debian.org/debian unstable/main armhf bsdextrautils armhf 2.40.4-2 [84.6 kB] Get: 28 http://deb.debian.org/debian unstable/main armhf libpipeline1 armhf 1.5.8-1 [35.0 kB] Get: 29 http://deb.debian.org/debian unstable/main armhf man-db armhf 2.13.0-1 [1382 kB] Get: 30 http://deb.debian.org/debian unstable/main armhf m4 armhf 1.4.19-5 [272 kB] Get: 31 http://deb.debian.org/debian unstable/main armhf autoconf all 2.72-3 [493 kB] Get: 32 http://deb.debian.org/debian unstable/main armhf autotools-dev all 20220109.1 [51.6 kB] Get: 33 http://deb.debian.org/debian unstable/main armhf automake all 1:1.17-2 [861 kB] Get: 34 http://deb.debian.org/debian unstable/main armhf autopoint all 0.23.1-1 [770 kB] Get: 35 http://deb.debian.org/debian unstable/main armhf libdebhelper-perl all 13.24.1 [90.9 kB] Get: 36 http://deb.debian.org/debian unstable/main armhf libtool all 2.5.4-2 [539 kB] Get: 37 http://deb.debian.org/debian unstable/main armhf dh-autoreconf all 20 [17.1 kB] Get: 38 http://deb.debian.org/debian unstable/main armhf libarchive-zip-perl all 1.68-1 [104 kB] Get: 39 http://deb.debian.org/debian unstable/main armhf libfile-stripnondeterminism-perl all 1.14.1-1 [19.6 kB] Get: 40 http://deb.debian.org/debian unstable/main armhf dh-strip-nondeterminism all 1.14.1-1 [8584 B] Get: 41 http://deb.debian.org/debian unstable/main armhf libelf1t64 armhf 0.192-4 [184 kB] Get: 42 http://deb.debian.org/debian unstable/main armhf dwz armhf 0.15-1+b2 [106 kB] Get: 43 http://deb.debian.org/debian unstable/main armhf libunistring5 armhf 1.3-1 [444 kB] Get: 44 http://deb.debian.org/debian unstable/main armhf libicu72 armhf 72.1-6 [9086 kB] Get: 45 http://deb.debian.org/debian unstable/main armhf libxml2 armhf 2.12.7+dfsg+really2.9.14-0.2+b1 [605 kB] Get: 46 http://deb.debian.org/debian unstable/main armhf gettext armhf 0.23.1-1 [1560 kB] Get: 47 http://deb.debian.org/debian unstable/main armhf intltool-debian all 0.35.0+20060710.6 [22.9 kB] Get: 48 http://deb.debian.org/debian unstable/main armhf po-debconf all 1.0.21+nmu1 [248 kB] Get: 49 http://deb.debian.org/debian unstable/main armhf debhelper all 13.24.1 [920 kB] Get: 50 http://deb.debian.org/debian unstable/main armhf python3-autocommand all 2.2.2-3 [13.6 kB] Get: 51 http://deb.debian.org/debian unstable/main armhf python3-more-itertools all 10.6.0-1 [65.3 kB] Get: 52 http://deb.debian.org/debian unstable/main armhf python3-typing-extensions all 4.12.2-2 [73.0 kB] Get: 53 http://deb.debian.org/debian unstable/main armhf python3-typeguard all 4.4.1-1 [37.0 kB] Get: 54 http://deb.debian.org/debian unstable/main armhf python3-inflect all 7.3.1-2 [32.4 kB] Get: 55 http://deb.debian.org/debian unstable/main armhf python3-jaraco.context all 6.0.0-1 [7984 B] Get: 56 http://deb.debian.org/debian unstable/main armhf python3-jaraco.functools all 4.1.0-1 [12.0 kB] Get: 57 http://deb.debian.org/debian unstable/main armhf python3-pkg-resources all 75.6.0-1 [222 kB] Get: 58 http://deb.debian.org/debian unstable/main armhf python3-jaraco.text all 4.0.0-1 [11.4 kB] Get: 59 http://deb.debian.org/debian unstable/main armhf python3-zipp all 3.21.0-1 [10.6 kB] Get: 60 http://deb.debian.org/debian unstable/main armhf python3-setuptools all 75.6.0-1 [720 kB] Get: 61 http://deb.debian.org/debian unstable/main armhf dh-python all 6.20250108 [113 kB] Get: 62 http://deb.debian.org/debian unstable/main armhf libbrotli1 armhf 1.1.0-2+b6 [282 kB] Get: 63 http://deb.debian.org/debian unstable/main armhf libidn2-0 armhf 2.3.7-2+b1 [125 kB] Get: 64 http://deb.debian.org/debian unstable/main armhf libp11-kit0 armhf 0.25.5-3 [385 kB] Get: 65 http://deb.debian.org/debian unstable/main armhf libtasn1-6 armhf 4.19.0-3+b3 [43.9 kB] Get: 66 http://deb.debian.org/debian unstable/main armhf libgnutls30t64 armhf 3.8.8-2 [1370 kB] Get: 67 http://deb.debian.org/debian unstable/main armhf libkrb5support0 armhf 1.21.3-4 [30.3 kB] Get: 68 http://deb.debian.org/debian unstable/main armhf libcom-err2 armhf 1.47.2-1 [23.3 kB] Get: 69 http://deb.debian.org/debian unstable/main armhf libk5crypto3 armhf 1.21.3-4 [75.8 kB] Get: 70 http://deb.debian.org/debian unstable/main armhf libkeyutils1 armhf 1.6.3-4 [8096 B] Get: 71 http://deb.debian.org/debian unstable/main armhf libkrb5-3 armhf 1.21.3-4 [285 kB] Get: 72 http://deb.debian.org/debian unstable/main armhf libgssapi-krb5-2 armhf 1.21.3-4 [116 kB] Get: 73 http://deb.debian.org/debian unstable/main armhf libsasl2-modules-db armhf 2.1.28+dfsg1-8+b1 [18.6 kB] Get: 74 http://deb.debian.org/debian unstable/main armhf libsasl2-2 armhf 2.1.28+dfsg1-8+b1 [50.6 kB] Get: 75 http://deb.debian.org/debian unstable/main armhf libldap2 armhf 2.6.9+dfsg-1 [167 kB] Get: 76 http://deb.debian.org/debian unstable/main armhf libnghttp2-14 armhf 1.64.0-1 [62.9 kB] Get: 77 http://deb.debian.org/debian unstable/main armhf libnghttp3-9 armhf 1.6.0-2 [56.8 kB] Get: 78 http://deb.debian.org/debian unstable/main armhf libngtcp2-16 armhf 1.9.1-1 [115 kB] Get: 79 http://deb.debian.org/debian unstable/main armhf libngtcp2-crypto-gnutls8 armhf 1.9.1-1 [15.8 kB] Get: 80 http://deb.debian.org/debian unstable/main armhf libpsl5t64 armhf 0.21.2-1.1+b1 [55.8 kB] Get: 81 http://deb.debian.org/debian unstable/main armhf librtmp1 armhf 2.4+20151223.gitfa8646d.1-2+b5 [53.2 kB] Get: 82 http://deb.debian.org/debian unstable/main armhf libssh2-1t64 armhf 1.11.1-1 [227 kB] Get: 83 http://deb.debian.org/debian unstable/main armhf libcurl3t64-gnutls armhf 8.11.1-1+b1 [311 kB] Get: 84 http://deb.debian.org/debian unstable/main armhf liberror-perl all 0.17029-2 [29.0 kB] Get: 85 http://deb.debian.org/debian unstable/main armhf git-man all 1:2.47.2-0.1 [2205 kB] Get: 86 http://deb.debian.org/debian unstable/main armhf git armhf 1:2.47.2-0.1 [6556 kB] Get: 87 http://deb.debian.org/debian unstable/main armhf libtirpc-common all 1.3.4+ds-1.3 [10.9 kB] Get: 88 http://deb.debian.org/debian unstable/main armhf libtirpc3t64 armhf 1.3.4+ds-1.3+b1 [71.3 kB] Get: 89 http://deb.debian.org/debian unstable/main armhf libnsl2 armhf 1.3.0-3+b3 [35.0 kB] Get: 90 http://deb.debian.org/debian unstable/main armhf libpython3.12-stdlib armhf 3.12.8-5 [1832 kB] Get: 91 http://deb.debian.org/debian unstable/main armhf localehelper all 0.1.4-3.1 [7928 B] Get: 92 http://deb.debian.org/debian unstable/main armhf python3.12 armhf 3.12.8-5 [677 kB] Get: 93 http://deb.debian.org/debian unstable/main armhf python3-all armhf 3.13.1-2 [1056 B] Get: 94 http://deb.debian.org/debian unstable/main armhf python3-ansi all 0.1.5-2 [6232 B] Get: 95 http://deb.debian.org/debian unstable/main armhf python3-bcrypt armhf 4.2.0-2.1 [214 kB] Get: 96 http://deb.debian.org/debian unstable/main armhf python3-blinker all 1.9.0-1 [12.6 kB] Get: 97 http://deb.debian.org/debian unstable/main armhf python3-bottle all 0.13.2-1.1 [54.3 kB] Get: 98 http://deb.debian.org/debian unstable/main armhf python3-soupsieve all 2.6-1 [38.3 kB] Get: 99 http://deb.debian.org/debian unstable/main armhf python3-bs4 all 4.12.3-3 [133 kB] Get: 100 http://deb.debian.org/debian unstable/main armhf python3-certifi all 2024.12.14+ds-1 [9624 B] Get: 101 http://deb.debian.org/debian unstable/main armhf python3-cffi-backend armhf 1.17.1-2+b1 [83.7 kB] Get: 102 http://deb.debian.org/debian unstable/main armhf python3-chardet all 5.2.0+dfsg-2 [108 kB] Get: 103 http://deb.debian.org/debian unstable/main armhf python3-charset-normalizer armhf 3.4.0-1+b1 [126 kB] Get: 104 http://deb.debian.org/debian unstable/main armhf python3-click all 8.1.8-1 [95.2 kB] Get: 105 http://deb.debian.org/debian unstable/main armhf python3-colorlog all 6.9.0-1 [27.5 kB] Get: 106 http://deb.debian.org/debian unstable/main armhf python3-cryptography armhf 43.0.0-1 [827 kB] Get: 107 http://deb.debian.org/debian unstable/main armhf python3-daemonize all 2.5.0-3 [6324 B] Get: 108 http://deb.debian.org/debian unstable/main armhf python3-urllib3 all 2.3.0-1 [114 kB] Get: 109 http://deb.debian.org/debian unstable/main armhf python3-dulwich armhf 0.22.7-1 [205 kB] Get: 110 http://deb.debian.org/debian unstable/main armhf python3-itsdangerous all 2.2.0-2 [18.1 kB] Get: 111 http://deb.debian.org/debian unstable/main armhf python3-markupsafe armhf 2.1.5-1+b2 [13.4 kB] Get: 112 http://deb.debian.org/debian unstable/main armhf python3-jinja2 all 3.1.5-1 [107 kB] Get: 113 http://deb.debian.org/debian unstable/main armhf python3-werkzeug all 3.1.3-2 [207 kB] Get: 114 http://deb.debian.org/debian unstable/main armhf python3-flask all 3.1.0-2 [106 kB] Get: 115 http://deb.debian.org/debian unstable/main armhf python3-idna all 3.8-2 [41.6 kB] Get: 116 http://deb.debian.org/debian unstable/main armhf python3-iniconfig all 1.1.1-2 [6396 B] Get: 117 http://deb.debian.org/debian unstable/main armhf python3-legacy-cgi all 2.6.1-2 [16.1 kB] Get: 118 http://deb.debian.org/debian unstable/main armhf python3-markdown all 3.7-2 [85.2 kB] Get: 119 http://deb.debian.org/debian unstable/main armhf python3-openssl all 25.0.0-1 [52.1 kB] Get: 120 http://deb.debian.org/debian unstable/main armhf python3-packaging all 24.2-1 [55.3 kB] Get: 121 http://deb.debian.org/debian unstable/main armhf python3-tempita all 0.6.0-1 [14.6 kB] Get: 122 http://deb.debian.org/debian unstable/main armhf python3-paste all 3.10.1-1 [222 kB] Get: 123 http://deb.debian.org/debian unstable/main armhf python3-pastedeploy-tpl all 3.1-1 [8268 B] Get: 124 http://deb.debian.org/debian unstable/main armhf python3-pastedeploy all 3.1-1 [18.3 kB] Get: 125 http://deb.debian.org/debian unstable/main armhf python3-pluggy all 1.5.0-1 [26.9 kB] Get: 126 http://deb.debian.org/debian unstable/main armhf python3-pygments all 2.18.0+dfsg-2 [836 kB] Get: 127 http://deb.debian.org/debian unstable/main armhf python3-pytest all 8.3.4-1 [250 kB] Get: 128 http://deb.debian.org/debian unstable/main armhf python3-requests all 2.32.3+dfsg-1 [71.9 kB] Get: 129 http://deb.debian.org/debian unstable/main armhf python3-waitress all 3.0.2-1 [46.5 kB] Get: 130 http://deb.debian.org/debian unstable/main armhf python3-webob all 1:1.8.9-1 [89.1 kB] Get: 131 http://deb.debian.org/debian unstable/main armhf python3-webtest all 3.0.2-1 [35.2 kB] Get: 132 http://deb.debian.org/debian unstable/main armhf python3-yapsy all 1.12.2-3 [30.4 kB] Fetched 54.8 MB in 8s (6575 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 ... 19568 files and directories currently installed.) Preparing to unpack .../libpython3.13-minimal_3.13.1-3_armhf.deb ... Unpacking libpython3.13-minimal:armhf (3.13.1-3) ... 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_armhf.deb ... Unpacking python3.13-minimal (3.13.1-3) ... Setting up libpython3.13-minimal:armhf (3.13.1-3) ... Setting up libexpat1:armhf (2.6.4-1) ... Setting up python3.13-minimal (3.13.1-3) ... 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 ... 19902 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_2025a-1_all.deb ... Unpacking tzdata (2025a-1) ... 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_armhf.deb ... Unpacking libpython3.13-stdlib:armhf (3.13.1-3) ... Selecting previously unselected package python3.13. Preparing to unpack .../8-python3.13_3.13.1-3_armhf.deb ... Unpacking python3.13 (3.13.1-3) ... 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 ... 20912 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_armhf.deb ... Unpacking libpython3.12-minimal:armhf (3.12.8-5) ... Selecting previously unselected package python3.12-minimal. Preparing to unpack .../002-python3.12-minimal_3.12.8-5_armhf.deb ... Unpacking python3.12-minimal (3.12.8-5) ... 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.17-2_all.deb ... Unpacking automake (1:1.17-2) ... 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_armhf.deb ... Unpacking libpython3.12-stdlib:armhf (3.12.8-5) ... 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_armhf.deb ... Unpacking python3.12 (3.12.8-5) ... 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) ... 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 (2025a-1) ... Current default time zone: 'Etc/UTC' Local time is now: Wed Jan 29 19:51:51 UTC 2025. Universal Time is now: Wed Jan 29 19:51:51 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.17-2) ... update-alternatives: using /usr/bin/automake-1.17 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) ... 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) ... 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) ... 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) ... 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) ... 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 <tchet@debian.org> 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/utils.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/streaming.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/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/backend_plugin_manager.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/repo_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/bootstrap.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/core.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/plugin_wizard.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 copying ./errbot/plugin_manager.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/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/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/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 copying ./errbot/backends/text.py -> /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 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/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/plugins.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/wsview.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/__init__.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/cnf_filter.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/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/flows.py -> /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/rendering copying ./errbot/rendering/__init__.py -> /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 creating /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 copying ./errbot/storage/base.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/storage 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/irc.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/test.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/backends copying ./errbot/backends/null.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-demo.css -> /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 creating /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/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/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/health.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/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/utils.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/chatRoom.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/vcheck.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/cnf_filter.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_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/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/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/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/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/webserver.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/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/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/status_plugins.md -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/core_plugins/templates copying ./errbot/storage/memory.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/storage copying ./errbot/storage/shelf.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/utils.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/streaming.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/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/backend_plugin_manager.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/repo_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/bootstrap.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/core.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/plugin_wizard.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 copying ./errbot/plugin_manager.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/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/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/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 copying ./errbot/backends/text.py -> /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 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/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/plugins.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/wsview.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/__init__.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/cnf_filter.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/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/flows.py -> /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/rendering copying ./errbot/rendering/__init__.py -> /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 creating /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 copying ./errbot/storage/base.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/storage 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/irc.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/test.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/backends copying ./errbot/backends/null.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-demo.css -> /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 creating /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/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/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/health.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/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/utils.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/chatRoom.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/vcheck.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/cnf_filter.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_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/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/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/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/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/webserver.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/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/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/status_plugins.md -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/core_plugins/templates copying ./errbot/storage/memory.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.13_errbot/build/errbot/storage copying ./errbot/storage/shelf.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 ........EEEEEEEEEEEEEEEEEEEE [ 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_echo __________________________ self = <multiprocessing.pool.ThreadPool state=INIT pool_size=5>, 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 = <DummyProcess(Thread-501 (worker), initial daemon)> 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x3160a8d0> 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 = <multiprocessing.pool.ThreadPool state=INIT pool_size=5>, 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 = <SubRequest 'testbot' for <Function test_echo>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x3160a8d0> 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-01-29 07:53:21,535 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:21,539 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:21,540 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:21,543 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:21,543 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:21,545 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_status_gc _______________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x734ff18> 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 = <DummyProcess(Thread-502 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_status_gc>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x734ff18> 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-01-29 07:53:21,893 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:21,898 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:21,899 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:21,901 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:21,902 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:21,921 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x325d4f8> 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 = <DummyProcess(Thread-503 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_config_cycle>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x325d4f8> 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-01-29 07:53:22,172 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:22,177 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:22,177 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:22,180 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:22,180 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:22,181 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x31a61b0> 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 = <DummyProcess(Thread-504 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_apropos>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x31a61b0> 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-01-29 07:53:22,439 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:22,444 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:22,444 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:22,447 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:22,448 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:22,448 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x325dc30> 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 = <DummyProcess(Thread-505 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_logtail>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x325dc30> 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-01-29 07:53:22,713 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:22,718 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:22,718 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:22,721 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:22,721 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:22,722 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x318be10> 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 = <DummyProcess(Thread-506 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_history>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x318be10> 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-01-29 07:53:23,209 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:23,214 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:23,215 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:23,218 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:23,219 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:23,219 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x319ea38> 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 = <DummyProcess(Thread-507 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_encoding_preservation>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x319ea38> 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-01-29 07:53:23,490 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:23,495 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:23,495 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:23,499 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:23,499 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:23,500 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x317d120> 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 = <DummyProcess(Thread-508 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_webserver_webhook_test>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x317d120> 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-01-29 07:53:23,780 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:23,785 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:23,785 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:23,789 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:23,789 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:23,790 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x30654f8> 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 = <DummyProcess(Thread-509 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_activate_reload_and_deactivate>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x30654f8> 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-01-29 07:53:24,129 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:24,134 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:24,135 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:24,138 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:24,138 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:24,139 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x31de0f0> 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 = <DummyProcess(Thread-510 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_unblacklist_and_blacklist>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x31de0f0> 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-01-29 07:53:24,415 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:24,421 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:24,421 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:24,425 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:24,425 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:24,426 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x3065ed0> 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 = <DummyProcess(Thread-511 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_optional_prefix>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x3065ed0> 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-01-29 07:53:25,006 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:25,013 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:25,013 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:25,112 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:25,113 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:25,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_optional_prefix_re_cmd _________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x2ff2d98> 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 = <DummyProcess(Thread-512 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_optional_prefix_re_cmd>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x2ff2d98> 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-01-29 07:53:25,411 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:25,417 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:25,417 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:25,421 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:25,421 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:25,422 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x31dca50> 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 = <DummyProcess(Thread-513 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_simple_match>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x31dca50> 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-01-29 07:53:25,704 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:25,710 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:25,711 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:25,714 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:25,715 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:25,716 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x2ff2900> 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 = <DummyProcess(Thread-514 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_no_suggest_on_re_commands>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x2ff2900> 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-01-29 07:53:26,002 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:26,007 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:26,008 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:26,013 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:26,013 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:26,014 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x325d888> 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 = <DummyProcess(Thread-515 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_callback_no_command>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x325d888> 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-01-29 07:53:26,349 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:26,354 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:26,355 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:26,358 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:26,359 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:26,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_subcommands ______________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0xf5f8a258> 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 = <DummyProcess(Thread-516 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_subcommands>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0xf5f8a258> 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-01-29 07:53:26,696 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:26,727 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:26,727 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:26,731 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:26,744 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:26,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_command_not_found_with_space_in_bot_prefix _______ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x302f048> 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 = <DummyProcess(Thread-517 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_command_not_found_with_space_in_bot_prefix>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x302f048> 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-01-29 07:53:27,035 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:27,040 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:27,041 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:27,045 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:27,046 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:27,046 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x1c4a6f78> 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 = <DummyProcess(Thread-518 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_mock_injection>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x1c4a6f78> 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-01-29 07:53:27,326 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:27,332 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:27,332 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:27,336 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:27,336 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:27,337 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x325d528> 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 = <DummyProcess(Thread-519 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_multiline_command>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x325d528> 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-01-29 07:53:27,625 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:27,630 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:27,630 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:27,634 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:27,634 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:27,635 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x1c4a68d0> 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 = <DummyProcess(Thread-520 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_plugin_info_command>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x1c4a68d0> 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-01-29 07:53:27,924 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:27,930 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:27,930 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:27,935 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:27,935 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:27,936 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x11bfe7c8> 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 = <DummyProcess(Thread-521 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_help_is_still_here>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x11bfe7c8> 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-01-29 07:53:28,234 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:28,239 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:28,239 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:28,243 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:28,243 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:28,244 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x26d536c0> 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 = <DummyProcess(Thread-522 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_echo_still_here>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x26d536c0> 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-01-29 07:53:28,573 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:28,587 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:28,588 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:28,592 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:28,592 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:28,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_bot_prefix_replaced __________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x26d53048> 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 = <DummyProcess(Thread-523 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_bot_prefix_replaced>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x26d53048> 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-01-29 07:53:28,904 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:28,910 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:28,911 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:28,915 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:28,915 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:28,916 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x507b6540> 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 = <DummyProcess(Thread-524 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_admins_to_notify>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x507b6540> 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-01-29 07:53:29,254 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:29,259 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:29,260 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:29,263 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:29,264 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:29,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_admins_not_notified __________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x51004288> 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 = <DummyProcess(Thread-525 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_admins_not_notified>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x51004288> 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-01-29 07:53:29,556 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:29,561 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:29,561 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:29,566 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:29,566 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:29,567 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x507a1e40> 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 = <DummyProcess(Thread-526 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_if_all_loaded_by_default>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x507a1e40> 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-01-29 07:53:29,843 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:29,848 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:29,848 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:29,852 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:29,852 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:29,854 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0xf22c37c8> 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 = <DummyProcess(Thread-527 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_single_dependency>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0xf22c37c8> 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-01-29 07:53:30,169 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:30,178 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:30,179 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:30,283 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:30,283 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:30,284 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x2edd528> 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 = <DummyProcess(Thread-528 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_double_dependency>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x2edd528> 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-01-29 07:53:30,590 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:30,596 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:30,596 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:30,600 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:30,600 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:30,602 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0xf40f6828> 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 = <DummyProcess(Thread-529 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_dependency_retrieval>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0xf40f6828> 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-01-29 07:53:30,884 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:30,890 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:30,890 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:30,894 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:30,894 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:30,895 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x2edd978> 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 = <DummyProcess(Thread-530 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_direct_circular_dependency>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x2edd978> 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-01-29 07:53:31,180 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:31,185 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:31,185 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:31,189 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:31,189 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:31,190 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x1c4a6648> 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 = <DummyProcess(Thread-531 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_indirect_circular_dependency>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x1c4a6648> 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-01-29 07:53:31,458 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:31,465 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:31,466 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:31,471 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:31,471 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:31,472 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x2ff2ac8> 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 = <DummyProcess(Thread-532 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_chained_dependency>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x2ff2ac8> 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-01-29 07:53:31,776 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:31,783 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:31,784 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:31,788 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:31,790 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:31,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_simple _________________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x1c4b68e8> 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 = <DummyProcess(Thread-533 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_simple>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x1c4b68e8> 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-01-29 07:53:32,111 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:32,116 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:32,116 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:32,122 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:32,122 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:32,123 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x50fec438> 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 = <DummyProcess(Thread-534 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_arg>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x50fec438> 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-01-29 07:53:32,414 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:32,419 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:32,419 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:32,423 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:32,424 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:32,425 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x2f35930> 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 = <DummyProcess(Thread-535 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_re>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x2f35930> 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-01-29 07:53:32,708 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:32,714 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:32,714 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:32,720 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:32,721 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:32,721 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x2f55f60> 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 = <DummyProcess(Thread-536 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_saw>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x2f55f60> 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-01-29 07:53:33,021 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:33,027 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:33,027 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:33,032 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:33,032 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:33,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_clashing ________________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x979e58>, 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 = <DummyProcess(Thread-537 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_clashing>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x979e58>, 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-01-29 07:53:33,357 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:33,362 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:33,363 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:33,367 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:33,367 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:33,368 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x2f372b8> 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 = <DummyProcess(Thread-538 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_list_flows>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x2f372b8> 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-01-29 07:53:33,663 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:33,673 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:33,673 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:33,682 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:33,682 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:33,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_no_autotrigger _____________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x30cf6a8> 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 = <DummyProcess(Thread-539 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_no_autotrigger>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x30cf6a8> 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-01-29 07:53:33,985 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:33,991 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:33,991 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:33,997 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:33,997 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:33,998 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x30adc30> 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 = <DummyProcess(Thread-540 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_autotrigger>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x30adc30> 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-01-29 07:53:34,328 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:34,335 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:34,335 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:34,340 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:34,342 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:34,343 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x3101a80> 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 = <DummyProcess(Thread-541 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_no_duplicate_autotrigger>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x3101a80> 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-01-29 07:53:34,639 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:34,645 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:34,645 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:34,650 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:34,650 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:34,651 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x30a8300> 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 = <DummyProcess(Thread-542 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_secondary_autotrigger>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x30a8300> 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-01-29 07:53:35,062 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:35,068 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:35,068 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:35,073 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:35,073 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:35,074 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x30a8330> 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 = <DummyProcess(Thread-543 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_manual_flow>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x30a8330> 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-01-29 07:53:35,397 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:35,403 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:35,404 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:35,411 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:35,412 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:35,421 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x31dc918> 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 = <DummyProcess(Thread-544 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_manual_flow_with_or_without_hinting>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x31dc918> 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-01-29 07:53:35,750 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:35,756 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:35,757 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:35,762 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:35,762 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:35,763 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x3101810> 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 = <DummyProcess(Thread-545 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_no_flyby_trigger_flow>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x3101810> 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-01-29 07:53:36,111 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:36,118 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:36,118 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:36,127 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:36,127 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:36,128 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x2f605e8> 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 = <DummyProcess(Thread-546 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_flow_only>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x2f605e8> 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-01-29 07:53:36,449 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:36,455 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:36,456 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:36,460 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:36,461 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:36,462 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0xaaf390>, 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 = <DummyProcess(Thread-547 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_flow_only_help>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0xaaf390>, 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-01-29 07:53:36,751 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:36,757 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:36,757 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:36,763 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:36,764 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:36,765 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0xf650b7c8> 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 = <DummyProcess(Thread-548 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_flows_stop>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0xf650b7c8> 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-01-29 07:53:37,054 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:37,060 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:37,061 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:37,066 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:37,066 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:37,067 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0xf650b7e0> 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 = <DummyProcess(Thread-549 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_flows_kill>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0xf650b7e0> 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-01-29 07:53:37,355 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:37,361 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:37,361 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:37,367 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:37,367 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:37,368 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0xa95e58>, 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 = <DummyProcess(Thread-550 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_room_flow>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0xa95e58>, 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-01-29 07:53:37,666 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:37,672 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:37,672 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:37,680 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:37,680 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:37,681 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x8d3348>, 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 = <DummyProcess(Thread-551 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_i18n_return>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x8d3348>, 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-01-29 07:53:37,979 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:37,988 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:37,988 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:37,994 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:37,995 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:37,996 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x936558>, 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 = <DummyProcess(Thread-552 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_i18n_simple_name>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x936558>, 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-01-29 07:53:38,303 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:38,309 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:38,310 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:38,315 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:38,315 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:38,316 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0xf5fb7f60> 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 = <DummyProcess(Thread-553 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_i18n_prefix>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0xf5fb7f60> 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-01-29 07:53:38,629 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:38,635 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:38,636 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:38,641 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:38,641 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:38,642 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x8eaff0>, 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 = <DummyProcess(Thread-554 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_i18n_suffix>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x8eaff0>, 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-01-29 07:53:38,951 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:38,958 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:38,959 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:38,964 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:38,964 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:38,965 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0xf6567348> 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 = <DummyProcess(Thread-555 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_linked_plugin_here>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0xf6567348> 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-01-29 07:53:39,267 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:39,273 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:39,273 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:39,280 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:39,281 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:39,282 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x7a1810>, 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 = <DummyProcess(Thread-556 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_botmatch_correct>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x7a1810>, 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-01-29 07:53:39,572 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:39,579 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:39,580 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:39,585 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:39,586 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:39,587 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0xf6567360> 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 = <DummyProcess(Thread-557 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_botmatch>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0xf6567360> 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-01-29 07:53:40,060 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:40,075 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:40,075 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:40,089 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:40,089 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:40,090 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0xf6567db0> 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 = <DummyProcess(Thread-558 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_foreign_mention>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0xf6567db0> 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-01-29 07:53:40,555 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:40,563 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:40,563 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:40,568 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:40,569 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:40,570 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x30a8678> 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 = <DummyProcess(Thread-559 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_testbot_mention>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x30a8678> 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-01-29 07:53:40,876 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:40,883 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:40,883 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:40,890 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:40,891 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:40,891 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x30d6078> 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 = <DummyProcess(Thread-560 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_multiple_mentions>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x30d6078> 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-01-29 07:53:41,219 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:41,224 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:41,225 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:41,230 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:41,231 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:41,231 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x51004918> 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 = <DummyProcess(Thread-561 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_plugin_methods>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x51004918> 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-01-29 07:53:41,516 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:41,521 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:41,522 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:41,527 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:41,527 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:41,528 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x30d6228> 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 = <DummyProcess(Thread-562 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_create_join_leave_destroy_lifecycle>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x30d6228> 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-01-29 07:53:41,817 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:41,822 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:41,822 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:41,827 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:41,828 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:41,829 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x705ed0>, 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 = <DummyProcess(Thread-563 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_occupants>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x705ed0>, 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-01-29 07:53:42,110 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:42,115 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:42,115 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:42,122 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:42,123 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:42,124 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x65d8a0>, 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 = <DummyProcess(Thread-564 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_topic>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x65d8a0>, 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-01-29 07:53:42,439 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:42,445 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:42,445 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:42,450 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:42,450 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:42,451 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x65d960>, 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 = <DummyProcess(Thread-565 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_plugin_callbacks>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x65d960>, 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-01-29 07:53:42,746 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:42,752 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:42,752 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:42,758 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:42,759 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:42,759 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x30d6318> 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 = <DummyProcess(Thread-566 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_botcommands>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x30d6318> 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-01-29 07:53:43,064 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:43,074 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:43,074 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:43,080 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:43,080 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:43,081 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x5cc198>, 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 = <DummyProcess(Thread-567 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_first>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x5cc198>, 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-01-29 07:53:43,361 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:43,366 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:43,366 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:43,374 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:43,374 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:43,375 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x5ae5d0>, 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 = <DummyProcess(Thread-568 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_second>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x5ae5d0>, 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-01-29 07:53:43,678 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:43,683 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:43,683 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:43,688 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:43,690 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:43,691 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x5ae6a8>, 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 = <DummyProcess(Thread-569 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_failed_config>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x5ae6a8>, 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-01-29 07:53:44,033 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:44,045 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:44,046 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:44,052 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:44,052 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:44,053 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x68ade0>, 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 = <DummyProcess(Thread-570 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_failed_config>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x68ade0>, 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-01-29 07:53:44,366 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:44,373 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:44,373 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:44,378 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:44,379 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:44,379 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x68ab58>, 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 = <DummyProcess(Thread-571 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_delayed_hello>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x68ab58>, 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-01-29 07:53:45,041 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:45,055 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:45,055 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:45,060 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:45,069 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:45,070 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x66af78>, 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 = <DummyProcess(Thread-572 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_delayed_hello_loop>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x66af78>, 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-01-29 07:53:45,456 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:45,470 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:45,471 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:45,480 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:45,481 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:45,482 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x5fd5b8>, 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 = <DummyProcess(Thread-623 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_nosyntax>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x5fd5b8>, 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-01-29 07:53:45,888 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:45,896 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:45,900 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:45,909 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:45,909 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:45,910 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x302d810> 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 = <DummyProcess(Thread-624 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_syntax>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x302d810> 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-01-29 07:53:46,212 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:46,218 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:46,219 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:46,226 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:46,226 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:46,227 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x31a6ff0> 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 = <DummyProcess(Thread-625 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_re_syntax>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x31a6ff0> 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-01-29 07:53:46,540 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:46,553 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:46,554 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:46,559 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:46,560 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:46,562 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x302d9f0> 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 = <DummyProcess(Thread-626 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_arg_syntax>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x302d9f0> 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-01-29 07:53:46,896 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:46,906 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:46,907 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:46,921 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:46,921 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:46,922 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x317db58> 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 = <DummyProcess(Thread-627 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_templates_1>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x317db58> 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-01-29 07:53:47,292 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:47,299 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:47,299 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:47,306 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:47,307 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:47,307 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x1c4a62a0> 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 = <DummyProcess(Thread-628 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_templates_2>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x1c4a62a0> 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-01-29 07:53:47,610 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:47,617 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:47,617 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:47,623 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:47,625 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:47,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_templates_3 ______________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x1c4a6168> 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 = <DummyProcess(Thread-629 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_templates_3>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x1c4a6168> 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-01-29 07:53:47,943 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:47,948 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:47,949 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:47,954 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:47,954 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:47,955 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x11bd6270> 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 = <DummyProcess(Thread-630 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_templates_4>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x11bd6270> 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-01-29 07:53:48,246 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:48,251 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:48,252 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:48,257 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:48,258 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:48,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_5 ______________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0xff613270> 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 = <DummyProcess(Thread-631 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_templates_5>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0xff613270> 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-01-29 07:53:48,529 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:48,535 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:48,535 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:48,542 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:48,542 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:48,543 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0xff62fc18> 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 = <DummyProcess(Thread-632 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_not_configured_url_returns_404>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0xff62fc18> 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-01-29 07:53:48,878 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:48,884 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:48,884 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:48,889 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:48,889 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:48,890 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0xff62c7c8> 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 = <DummyProcess(Thread-633 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_webserver_plugin_ok>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0xff62c7c8> 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-01-29 07:53:49,180 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:49,185 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:49,186 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:49,191 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:49,191 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:49,192 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0xff62ff30> 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 = <DummyProcess(Thread-634 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_trailing_no_slash_ok>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0xff62ff30> 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-01-29 07:53:49,498 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:49,503 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:49,504 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:49,509 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:49,510 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:49,511 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0xff41a480> 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 = <DummyProcess(Thread-635 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_trailing_slash_also_ok>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0xff41a480> 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-01-29 07:53:49,822 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:49,827 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:49,827 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:49,980 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:49,981 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:49,982 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x317d978> 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 = <DummyProcess(Thread-636 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_json_is_automatically_decoded>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x317d978> 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-01-29 07:53:50,275 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:50,281 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:50,282 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:50,288 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:50,288 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:50,289 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x317df30> 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 = <DummyProcess(Thread-637 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_json_on_custom_url_is_automatically_decoded>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x317df30> 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-01-29 07:53:50,598 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:50,604 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:50,604 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:50,611 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:50,612 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:50,617 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x31a6150> 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 = <DummyProcess(Thread-638 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_post_form_on_webhook_without_form_param_is_automatically_decoded>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x31a6150> 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-01-29 07:53:50,932 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:50,939 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:50,939 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:50,946 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:50,946 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:50,947 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0xf6538f30> 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 = <DummyProcess(Thread-639 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_post_form_on_webhook_with_custom_url_and_without_form_param_is_automatically_decoded>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0xf6538f30> 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-01-29 07:53:51,257 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:51,263 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:51,264 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:51,270 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:51,271 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:51,272 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x302ff60> 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 = <DummyProcess(Thread-640 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_webhooks_with_form_parameter_decode_json_automatically>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x302ff60> 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-01-29 07:53:51,598 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:51,604 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:51,604 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:51,613 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:51,613 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:51,614 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0xff62c828> 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 = <DummyProcess(Thread-641 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_webhooks_with_form_parameter_on_custom_url_decode_json_automatically>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0xff62c828> 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-01-29 07:53:51,949 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:51,957 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:51,957 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:51,966 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:51,967 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:51,968 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0xff41aac8> 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 = <DummyProcess(Thread-642 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_webhooks_with_raw_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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0xff41aac8> 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-01-29 07:53:52,269 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:52,276 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:52,278 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:52,285 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:52,285 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:52,286 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0xff41aab0> 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 = <DummyProcess(Thread-643 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_webhooks_with_naked_decorator_raw_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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0xff41aab0> 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-01-29 07:53:52,588 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:52,594 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:52,595 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:52,601 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:52,601 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:52,602 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0xff62f270> 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 = <DummyProcess(Thread-644 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_generate_certificate_creates_usable_cert>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0xff62f270> 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-01-29 07:53:52,899 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:52,906 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:52,907 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:52,914 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:52,914 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:52,915 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0xff6a19a8> 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 = <DummyProcess(Thread-645 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_custom_headers_and_status_codes>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0xff6a19a8> 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-01-29 07:53:53,222 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:53,229 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:53,229 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:53,236 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:53,237 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:53,238 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0xff471468> 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 = <DummyProcess(Thread-646 (worker), initial daemon)> 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 = <SubRequest 'testbot' for <Function test_lambda_webhook>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0xff471468> 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-01-29 07:53:53,542 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:53:53,548 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:53:53,550 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:53:53,556 DEBUG errbot.core ErrBot init. 2025-01-29 07:53:53,556 DEBUG errbot.backends.base Backend init. 2025-01-29 07:53:53,558 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 = <Thread(Thread-573 (streamer), initial)> 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_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, 110 passed, 8 deselected, 2 warnings, 96 errors in 38.91s ====== 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 ........EEEEEEEEEEEEEEEEEEEE [ 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_echo __________________________ self = <multiprocessing.pool.ThreadPool state=INIT pool_size=5>, 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.process_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.13/multiprocessing/pool.py:215: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-501 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x3b4dedf0> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <multiprocessing.pool.ThreadPool state=INIT pool_size=5>, 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.process_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.13/multiprocessing/pool.py:219: AttributeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_echo>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x3b4dedf0> 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.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 219, in __init__ p.terminate() ^^^^^^^^^^^ AttributeError: 'DummyProcess' object has no attribute 'terminate' ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:03,447 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:03,453 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:03,453 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:03,457 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:03,457 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:03,462 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 219, in __init__ p.terminate() ^^^^^^^^^^^ AttributeError: 'DummyProcess' object has no attribute 'terminate' _______________________ ERROR at setup of test_status_gc _______________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x465df870> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-502 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_status_gc>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x465df870> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:03,765 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:03,770 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:03,771 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:03,774 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:03,774 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:03,799 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread _____________________ ERROR at setup of test_config_cycle ______________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x30c4da80> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-503 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_config_cycle>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x30c4da80> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:04,079 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:04,084 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:04,085 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:04,087 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:04,088 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:04,088 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ________________________ ERROR at setup of test_apropos ________________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x465dff50> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-504 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_apropos>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x465dff50> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:04,330 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:04,336 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:04,336 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:04,339 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:04,339 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:04,340 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ________________________ ERROR at setup of test_logtail ________________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x1c2f6d40> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-505 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_logtail>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x1c2f6d40> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:04,579 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:04,586 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:04,586 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:04,588 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:04,589 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:04,589 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ________________________ ERROR at setup of test_history ________________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x465df240> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-506 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_history>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x465df240> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:04,829 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:04,835 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:04,835 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:04,838 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:04,839 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:04,840 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread _________________ ERROR at setup of test_encoding_preservation _________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x30c4da80> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-507 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_encoding_preservation>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x30c4da80> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:05,076 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:05,081 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:05,082 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:05,084 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:05,085 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:05,085 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ________________ ERROR at setup of test_webserver_webhook_test _________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x465dfea0> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-508 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_webserver_webhook_test>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x465dfea0> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:05,320 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:05,326 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:05,326 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:05,329 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:05,329 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:05,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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ____________ ERROR at setup of test_activate_reload_and_deactivate _____________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x719e870> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-509 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_activate_reload_and_deactivate>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x719e870> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:05,587 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:05,592 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:05,592 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:05,595 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:05,595 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:05,596 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread _______________ ERROR at setup of test_unblacklist_and_blacklist _______________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x465df190> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-510 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_unblacklist_and_blacklist>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x465df190> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:05,865 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:05,871 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:05,871 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:05,874 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:05,874 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:05,875 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ____________________ ERROR at setup of test_optional_prefix ____________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x465dfc90> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-511 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_optional_prefix>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x465dfc90> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:06,115 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:06,121 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:06,121 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:06,124 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:06,124 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:06,125 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ________________ ERROR at setup of test_optional_prefix_re_cmd _________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x465dfb30> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-512 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_optional_prefix_re_cmd>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x465dfb30> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:06,380 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:06,385 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:06,386 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:06,388 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:06,389 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:06,390 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread _____________________ ERROR at setup of test_simple_match ______________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x2703ea0> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-513 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_simple_match>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x2703ea0> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:06,642 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:06,648 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:06,648 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:06,651 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:06,651 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:06,652 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread _______________ ERROR at setup of test_no_suggest_on_re_commands _______________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x465df190> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-514 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_no_suggest_on_re_commands>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x465df190> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:06,896 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:06,904 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:06,904 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:06,907 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:06,908 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:06,908 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread __________________ ERROR at setup of test_callback_no_command __________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x2703920> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-515 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_callback_no_command>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x2703920> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:07,193 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:07,202 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:07,203 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:07,206 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:07,206 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:07,207 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ______________________ ERROR at setup of test_subcommands ______________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x465dfc90> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-516 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_subcommands>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x465dfc90> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:07,466 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:07,472 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:07,473 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:07,476 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:07,476 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:07,477 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ______ ERROR at setup of test_command_not_found_with_space_in_bot_prefix _______ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x27cfdf0> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-517 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_command_not_found_with_space_in_bot_prefix>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x27cfdf0> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:07,736 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:07,742 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:07,743 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:07,746 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:07,746 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:07,747 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ____________________ ERROR at setup of test_mock_injection _____________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x465dfc90> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-518 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_mock_injection>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x465dfc90> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:08,000 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:08,007 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:08,007 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:08,011 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:08,011 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:08,012 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ___________________ ERROR at setup of test_multiline_command ___________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x27cf500> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-519 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_multiline_command>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x27cf500> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:08,262 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:08,269 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:08,269 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:08,272 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:08,273 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:08,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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread __________________ ERROR at setup of test_plugin_info_command __________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x465dfc90> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-520 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_plugin_info_command>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x465dfc90> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:08,537 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:08,544 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:08,544 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:08,548 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:08,548 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:08,549 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread __________________ ERROR at setup of test_help_is_still_here ___________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x263d920> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-521 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_help_is_still_here>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x263d920> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:08,825 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:08,832 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:08,832 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:08,836 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:08,836 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:08,837 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ____________________ ERROR at setup of test_echo_still_here ____________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x465dfc90> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-522 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_echo_still_here>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x465dfc90> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:09,106 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:09,112 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:09,113 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:09,116 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:09,116 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:09,117 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread __________________ ERROR at setup of test_bot_prefix_replaced __________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x263d710> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-523 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_bot_prefix_replaced>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x263d710> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:09,553 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:09,563 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:09,563 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:09,567 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:09,567 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:09,568 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ___________________ ERROR at setup of test_admins_to_notify ____________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x27cfdf0> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-524 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_admins_to_notify>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x27cfdf0> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:09,883 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:09,899 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:09,899 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:09,903 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:09,903 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:09,904 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread __________________ ERROR at setup of test_admins_not_notified __________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x27035b0> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-525 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_admins_not_notified>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x27035b0> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:10,200 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:10,207 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:10,207 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:10,211 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:10,211 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:10,212 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread _______________ ERROR at setup of test_if_all_loaded_by_default ________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x27cf9d0> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-526 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_if_all_loaded_by_default>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x27cf9d0> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:10,477 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:10,483 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:10,484 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:10,487 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:10,487 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:10,488 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ___________________ ERROR at setup of test_single_dependency ___________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x27035b0> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-527 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_single_dependency>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x27035b0> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:10,773 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:10,780 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:10,780 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:10,784 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:10,784 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:10,786 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ___________________ ERROR at setup of test_double_dependency ___________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x263d710> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-528 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_double_dependency>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x263d710> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:11,081 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:11,089 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:11,090 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:11,093 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:11,094 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:11,095 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread _________________ ERROR at setup of test_dependency_retrieval __________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x30c4d030> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-529 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_dependency_retrieval>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x30c4d030> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:11,394 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:11,404 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:11,404 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:11,411 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:11,411 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:11,413 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ______________ ERROR at setup of test_direct_circular_dependency _______________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x27035b0> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-530 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_direct_circular_dependency>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x27035b0> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:11,718 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:11,725 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:11,726 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:11,730 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:11,730 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:11,731 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread _____________ ERROR at setup of test_indirect_circular_dependency ______________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x719e870> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-531 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_indirect_circular_dependency>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x719e870> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:11,988 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:11,994 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:11,995 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:11,999 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:11,999 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:12,000 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread __________________ ERROR at setup of test_chained_dependency ___________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x5af31190> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-532 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_chained_dependency>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x5af31190> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:12,269 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:12,284 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:12,284 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:12,288 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:12,288 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:12,293 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ________________________ ERROR at setup of test_simple _________________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x263d920> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-533 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_simple>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x263d920> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:12,582 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:12,589 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:12,589 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:12,593 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:12,593 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:12,594 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread __________________________ ERROR at setup of test_arg __________________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x27035b0> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-534 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_arg>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x27035b0> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:12,844 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:12,851 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:12,851 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:12,855 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:12,855 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:12,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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread __________________________ ERROR at setup of test_re ___________________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x30c4dd40> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-535 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_re>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x30c4dd40> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:13,180 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:13,189 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:13,190 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:13,196 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:13,196 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:13,197 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread __________________________ ERROR at setup of test_saw __________________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x5b7712f0> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-536 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_saw>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x5b7712f0> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:13,494 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:13,505 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:13,505 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:13,517 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:13,518 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:13,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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread _______________________ ERROR at setup of test_clashing ________________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0xf6564d40> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-537 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_clashing>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0xf6564d40> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:13,863 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:13,870 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:13,870 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:13,874 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:13,875 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:13,876 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ______________________ ERROR at setup of test_list_flows _______________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x5af31190> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-538 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_list_flows>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x5af31190> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:14,133 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:14,139 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:14,139 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:14,143 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:14,143 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:14,144 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ____________________ ERROR at setup of test_no_autotrigger _____________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0xf6564be0> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-539 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_no_autotrigger>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0xf6564be0> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:14,415 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:14,422 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:14,423 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:14,427 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:14,428 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:14,429 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ______________________ ERROR at setup of test_autotrigger ______________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x2819660> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-540 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_autotrigger>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x2819660> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:14,681 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:14,688 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:14,688 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:14,693 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:14,693 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:14,694 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread _______________ ERROR at setup of test_no_duplicate_autotrigger ________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0xf6564d40> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-541 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_no_duplicate_autotrigger>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0xf6564d40> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:14,977 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:14,989 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:14,989 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:14,993 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:14,994 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:14,995 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread _________________ ERROR at setup of test_secondary_autotrigger _________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x2819660> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-542 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_secondary_autotrigger>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x2819660> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:15,248 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:15,254 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:15,254 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:15,258 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:15,258 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:15,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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ______________________ ERROR at setup of test_manual_flow ______________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x30c4dd40> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-543 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_manual_flow>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x30c4dd40> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:15,520 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:15,526 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:15,527 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:15,531 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:15,531 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:15,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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread __________ ERROR at setup of test_manual_flow_with_or_without_hinting __________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x2819660> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-544 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_manual_flow_with_or_without_hinting>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x2819660> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:15,791 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:15,800 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:15,800 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:15,804 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:15,804 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:15,805 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread _________________ ERROR at setup of test_no_flyby_trigger_flow _________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0xf6564be0> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-545 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_no_flyby_trigger_flow>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0xf6564be0> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:16,047 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:16,052 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:16,052 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:16,056 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:16,065 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:16,066 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread _______________________ ERROR at setup of test_flow_only _______________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x24f6710> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-546 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_flow_only>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x24f6710> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:16,346 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:16,352 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:16,352 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:16,356 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:16,356 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:16,357 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ____________________ ERROR at setup of test_flow_only_help _____________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x263d710> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-547 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_flow_only_help>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x263d710> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:16,601 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:16,606 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:16,606 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:16,611 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:16,611 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:16,612 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ______________________ ERROR at setup of test_flows_stop _______________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0xf6564be0> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-548 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_flows_stop>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0xf6564be0> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:16,880 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:16,885 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:16,885 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:16,890 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:16,891 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:16,892 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ______________________ ERROR at setup of test_flows_kill _______________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0xf49d9920> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-549 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_flows_kill>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0xf49d9920> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:17,266 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:17,275 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:17,275 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:17,279 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:17,280 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:17,281 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread _______________________ ERROR at setup of test_room_flow _______________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x719e870> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-550 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_room_flow>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x719e870> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:17,540 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:17,545 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:17,546 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:17,550 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:17,550 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:17,551 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ______________________ ERROR at setup of test_i18n_return ______________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0xf6564d40> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-551 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_i18n_return>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0xf6564d40> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:17,804 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:17,809 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:17,810 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:17,814 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:17,814 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:17,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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ___________________ ERROR at setup of test_i18n_simple_name ____________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x5b791a80> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-552 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_i18n_simple_name>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x5b791a80> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:18,062 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:18,068 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:18,069 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:18,073 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:18,073 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:18,074 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ______________________ ERROR at setup of test_i18n_prefix ______________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x719e870> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-553 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_i18n_prefix>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x719e870> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:18,372 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:18,379 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:18,379 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:18,383 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:18,384 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:18,385 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ______________________ ERROR at setup of test_i18n_suffix ______________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x5b791a80> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-554 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_i18n_suffix>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x5b791a80> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:18,669 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:18,675 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:18,675 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:18,679 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:18,680 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:18,680 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread __________________ ERROR at setup of test_linked_plugin_here ___________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x7662f0>, 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-555 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_linked_plugin_here>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x7662f0>, 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:18,944 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:18,950 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:18,950 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:18,954 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:18,955 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:18,956 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ___________________ ERROR at setup of test_botmatch_correct ____________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x27035b0> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-556 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_botmatch_correct>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x27035b0> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:19,213 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:19,219 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:19,219 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:19,223 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:19,223 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:19,224 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread _______________________ ERROR at setup of test_botmatch ________________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x24f6ea0> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-557 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_botmatch>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x24f6ea0> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:19,457 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:19,462 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:19,463 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:19,467 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:19,467 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:19,468 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ____________________ ERROR at setup of test_foreign_mention ____________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x5b791a80> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-558 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_foreign_mention>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x5b791a80> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:19,723 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:19,730 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:19,730 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:19,735 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:19,736 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:19,737 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ____________________ ERROR at setup of test_testbot_mention ____________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x27035b0> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-559 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_testbot_mention>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x27035b0> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:19,983 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:19,989 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:19,989 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:19,994 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:19,994 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:19,995 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ___________________ ERROR at setup of test_multiple_mentions ___________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x5b771500> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-560 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_multiple_mentions>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x5b771500> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:20,235 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:20,241 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:20,241 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:20,245 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:20,246 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:20,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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ____________________ ERROR at setup of test_plugin_methods _____________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x27035b0> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-561 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_plugin_methods>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x27035b0> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:20,501 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:20,507 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:20,507 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:20,512 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:20,512 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:20,513 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread __________ ERROR at setup of test_create_join_leave_destroy_lifecycle __________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x24f6ea0> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-562 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_create_join_leave_destroy_lifecycle>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x24f6ea0> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:20,755 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:20,761 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:20,761 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:20,766 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:20,767 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:20,767 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread _______________________ ERROR at setup of test_occupants _______________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x27035b0> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-563 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_occupants>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x27035b0> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:21,027 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:21,034 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:21,034 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:21,039 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:21,039 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:21,040 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread _________________________ ERROR at setup of test_topic _________________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x6f9c90>, 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-564 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_topic>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x6f9c90>, 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:21,297 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:21,303 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:21,303 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:21,308 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:21,308 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:21,309 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ___________________ ERROR at setup of test_plugin_callbacks ____________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x27035b0> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-565 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_plugin_callbacks>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x27035b0> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:21,547 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:21,553 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:21,553 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:21,557 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:21,558 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:21,558 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ______________________ ERROR at setup of test_botcommands ______________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x6f9870>, 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-566 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_botcommands>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x6f9870>, 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:21,810 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:21,816 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:21,818 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:21,822 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:21,822 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:21,823 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread _________________________ ERROR at setup of test_first _________________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x27035b0> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-567 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_first>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x27035b0> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:22,086 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:22,094 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:22,094 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:22,099 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:22,099 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:22,100 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ________________________ ERROR at setup of test_second _________________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x526be0>, 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-568 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_second>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x526be0>, 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:22,381 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:22,389 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:22,391 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:22,397 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:22,398 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:22,399 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread _____________________ ERROR at setup of test_failed_config _____________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x27035b0> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-569 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_failed_config>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x27035b0> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:22,751 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:22,758 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:22,758 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:22,763 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:22,763 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:22,764 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread _____________________ ERROR at setup of test_failed_config _____________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x5263a0>, 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-570 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_failed_config>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x5263a0>, 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:23,012 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:23,017 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:23,018 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:23,023 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:23,023 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:23,024 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread _____________________ ERROR at setup of test_delayed_hello _____________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0xff4d3870> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-571 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_delayed_hello>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0xff4d3870> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:23,385 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:23,393 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:23,394 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:23,402 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:23,402 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:23,403 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread __________________ ERROR at setup of test_delayed_hello_loop ___________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0xff4d3a80> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-572 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_delayed_hello_loop>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0xff4d3a80> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:23,751 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:23,760 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:23,760 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:23,767 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:23,769 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:23,770 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread _______________________ ERROR at setup of test_nosyntax ________________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x6f9c90>, 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-623 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_nosyntax>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x6f9c90>, 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:24,309 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:24,316 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:24,321 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:24,330 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:24,330 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:24,331 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ________________________ ERROR at setup of test_syntax _________________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x259bbe0> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-624 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_syntax>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x259bbe0> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:24,646 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:24,653 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:24,653 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:24,657 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:24,658 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:24,659 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread _______________________ ERROR at setup of test_re_syntax _______________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x6f9d40>, 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-625 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_re_syntax>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x6f9d40>, 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:24,928 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:24,934 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:24,934 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:24,939 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:24,939 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:24,940 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ______________________ ERROR at setup of test_arg_syntax _______________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0xf6564be0> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-626 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_arg_syntax>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0xf6564be0> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:25,222 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:25,228 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:25,228 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:25,232 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:25,233 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:25,234 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ______________________ ERROR at setup of test_templates_1 ______________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x719e9d0> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-627 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_templates_1>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x719e9d0> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:25,511 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:25,517 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:25,518 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:25,522 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:25,522 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:25,523 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ______________________ ERROR at setup of test_templates_2 ______________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x6f9d40>, 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-628 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_templates_2>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x6f9d40>, 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:25,797 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:25,804 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:25,804 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:25,809 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:25,809 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:25,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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ______________________ ERROR at setup of test_templates_3 ______________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x719ea80> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-629 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_templates_3>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x719ea80> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:26,066 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:26,072 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:26,072 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:26,079 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:26,079 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:26,080 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ______________________ ERROR at setup of test_templates_4 ______________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x6f9c90>, 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-630 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_templates_4>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x6f9c90>, 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:26,345 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:26,353 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:26,354 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:26,359 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:26,359 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:26,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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ______________________ ERROR at setup of test_templates_5 ______________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x1c2f67c0> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-631 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_templates_5>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x1c2f67c0> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:26,637 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:26,643 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:26,643 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:26,649 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:26,650 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:26,650 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ____________ ERROR at setup of test_not_configured_url_returns_404 _____________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x3b4de500> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-632 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_not_configured_url_returns_404>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x3b4de500> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:26,974 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:26,980 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:26,980 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:26,986 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:26,986 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:26,987 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread __________________ ERROR at setup of test_webserver_plugin_ok __________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x6f9450>, 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-633 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_webserver_plugin_ok>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x6f9450>, 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:27,274 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:27,280 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:27,281 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:27,286 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:27,286 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:27,287 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread _________________ ERROR at setup of test_trailing_no_slash_ok __________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x465df0e0> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-634 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_trailing_no_slash_ok>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x465df0e0> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:27,545 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:27,551 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:27,552 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:27,557 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:27,557 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:27,558 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ________________ ERROR at setup of test_trailing_slash_also_ok _________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x6f9870>, 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-635 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_trailing_slash_also_ok>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x6f9870>, 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:27,841 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:27,848 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:27,848 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:27,853 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:27,854 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:27,855 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread _____________ ERROR at setup of test_json_is_automatically_decoded _____________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x465df0e0> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-636 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_json_is_automatically_decoded>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x465df0e0> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:28,125 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:28,132 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:28,133 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:28,138 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:28,138 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:28,139 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ______ ERROR at setup of test_json_on_custom_url_is_automatically_decoded ______ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x719e9d0> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-637 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_json_on_custom_url_is_automatically_decoded>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x719e9d0> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:28,394 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:28,401 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:28,401 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:28,406 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:28,407 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:28,408 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x465df0e0> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-638 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_post_form_on_webhook_without_form_param_is_automatically_decoded>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x465df0e0> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:28,662 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:28,668 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:28,670 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:28,675 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:28,675 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:28,676 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x3b4de500> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-639 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_post_form_on_webhook_with_custom_url_and_without_form_param_is_automatically_decoded>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x3b4de500> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:28,940 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:28,946 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:28,946 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:28,953 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:28,954 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:28,955 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread _ ERROR at setup of test_webhooks_with_form_parameter_decode_json_automatically _ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x30c4d240> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-640 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_webhooks_with_form_parameter_decode_json_automatically>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x30c4d240> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:29,236 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:29,243 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:29,244 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:29,249 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:29,250 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:29,252 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ 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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x5af319d0> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-641 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_webhooks_with_form_parameter_on_custom_url_decode_json_automatically>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x5af319d0> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:29,496 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:29,503 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:29,503 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:29,509 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:29,509 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:29,510 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread _______________ ERROR at setup of test_webhooks_with_raw_request _______________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x465df0e0> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-642 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_webhooks_with_raw_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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x465df0e0> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:29,842 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:29,849 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:29,850 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:29,855 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:29,855 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:29,857 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread _______ ERROR at setup of test_webhooks_with_naked_decorator_raw_request _______ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x5b771710> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-643 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_webhooks_with_naked_decorator_raw_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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x5b771710> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:30,146 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:30,153 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:30,153 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:30,159 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:30,160 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:30,161 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread _______ ERROR at setup of test_generate_certificate_creates_usable_cert ________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x2703240> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-644 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_generate_certificate_creates_usable_cert>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x2703240> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:30,414 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:30,421 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:30,421 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:30,427 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:30,427 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:30,428 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ____________ ERROR at setup of test_custom_headers_and_status_codes ____________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x30c4d240> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-645 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_custom_headers_and_status_codes>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0x30c4d240> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:30,672 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:30,679 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:30,679 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:30,685 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:30,685 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:30,686 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ____________________ ERROR at setup of test_lambda_webhook _____________________ backend_name = 'Test', logger = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0xf2b4c3a0> 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.13/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.13/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.13/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.13/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.13/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <DummyProcess(Thread-646 (worker), stopped daemon)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: RuntimeError During handling of the above exception, another exception occurred: request = <SubRequest 'testbot' for <Function test_lambda_webhook>> @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 = <RootLogger root (DEBUG)> config = <errbot.backends.test.ShallowConfig object at 0xf2b4c3a0> 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2025-01-29 07:54:31,136 INFO errbot.bootstrap Found Storage plugin: Memory. 2025-01-29 07:54:31,143 INFO errbot.bootstrap Found Backend plugin: Test 2025-01-29 07:54:31,144 DEBUG errbot.storage Opening storage 'repomgr' 2025-01-29 07:54:31,148 DEBUG errbot.core ErrBot init. 2025-01-29 07:54:31,149 DEBUG errbot.backends.base Backend init. 2025-01-29 07:54:31,150 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.13/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() ~~~~~~~~~~~~~~~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ self._processes, ^^^^^^^^^^^^^^^^ ...<3 lines>... self._maxtasksperchild, ^^^^^^^^^^^^^^^^^^^^^^^ self._wrap_exception) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() ~~~~~~~^^ File "/usr/lib/python3.13/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/usr/lib/python3.13/threading.py", line 973, in start _start_joinable_thread(self._bootstrap, handle=self._handle, ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ daemon=self.daemon) ^^^^^^^^^^^^^^^^^^^ 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 = <Thread(Thread-573 (streamer), stopped)> 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 joinable thread > _start_joinable_thread(self._bootstrap, handle=self._handle, daemon=self.daemon) E RuntimeError: can't start new thread /usr/lib/python3.13/threading.py:973: 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 -- 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_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, 110 passed, 8 deselected, 1 warning, 96 errors in 35.49s ====== E: pybuild pybuild:389: test: plugin distutils failed with: exit code=1: 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-BqDb7b_O 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/27481 and its subdirectories Wed Jan 29 19:54:56 UTC 2025 W: No second build log, what happened?